com/hypixel/hytale/storage/IndexedStorageFile.java
package com.hypixel.hytale.storage;
import com.github.luben.zstd.Zstd;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.metrics.MetricsRegistry;
import com.hypixel.hytale.unsafe.UnsafeUtil;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.StampedLock;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class IndexedStorageFile implements Closeable {
public static final StampedLock[] EMPTY_STAMPED_LOCKS = new StampedLock[0];
public static final MetricsRegistry<IndexedStorageFile> METRICS_REGISTRY;
public static final String MAGIC_STRING = "HytaleIndexedStorage";
public static final int VERSION = 1;
public static final int DEFAULT_BLOB_COUNT = 1024;
public static final int DEFAULT_SEGMENT_SIZE = 4096;
public static final int DEFAULT_COMPRESSION_LEVEL = 3;
static final OffsetHelper HOH;
public static final int MAGIC_LENGTH = 20;
public static final int MAGIC_OFFSET;
public static final int VERSION_OFFSET;
public static final int BLOB_COUNT_OFFSET;
public static final int SEGMENT_SIZE_OFFSET;
public static final int HEADER_LENGTH;
static final OffsetHelper BOH;
public static final int SRC_LENGTH_OFFSET;
public static final int COMPRESSED_LENGTH_OFFSET;
public static final int BLOB_HEADER_LENGTH;
public static final int INDEX_SIZE = 4;
public static final int UNASSIGNED_INDEX = 0;
public static final int FIRST_SEGMENT_INDEX = 1;
public static final FileAttribute<?>[] NO_ATTRIBUTES;
static final byte[] MAGIC_BYTES;
private static final ByteBuffer MAGIC_BUFFER;
private static final ThreadLocal<ByteBuffer> CACHED_TEMP_BUFFER;
@Nonnull
private final Path path;
private final FileChannel fileChannel;
private boolean flushOnWrite = false;
private int compressionLevel = 3;
private int version;
private int blobCount;
private int segmentSize;
private StampedLock[] indexLocks;
private MappedByteBuffer mappedBlobIndexes;
private final StampedLock segmentLocksLock = new StampedLock();
private StampedLock[] segmentLocks;
private final StampedLock usedSegmentsLock;
private final BitSet usedSegments;
@Nonnull
private static ByteBuffer getTempBuffer(int length) {
ByteBuffer buffer = (ByteBuffer)CACHED_TEMP_BUFFER.get();
buffer.position(0);
buffer.limit(length);
return buffer;
}
@Nonnull
private static ByteBuffer allocateDirect(int length) {
return ByteBuffer.allocateDirect(length);
}
@Nonnull
public static IndexedStorageFile open(@Nonnull Path path, OpenOption... options) throws IOException {
return open(path, 1024, 4096, Set.of(options), NO_ATTRIBUTES);
}
@Nonnull
public static IndexedStorageFile open(@Nonnull Path path, @Nonnull Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
return open(path, 1024, 4096, options, attrs);
}
@Nonnull
public static IndexedStorageFile open(@Nonnull Path path, int blobCount, int segmentSize, OpenOption... options) throws IOException {
return open(path, blobCount, segmentSize, Set.of(options), NO_ATTRIBUTES);
}
@Nonnull
public static IndexedStorageFile open(@Nonnull Path path, int blobCount, int segmentSize, @Nonnull Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
IndexedStorageFile storageFile = new IndexedStorageFile(path, FileChannel.open(path, options, attrs));
if (options.contains(StandardOpenOption.CREATE_NEW)) {
storageFile.create(blobCount, segmentSize);
return storageFile;
} else {
if (options.contains(StandardOpenOption.CREATE) && storageFile.fileChannel.size() == 0L) {
storageFile.create(blobCount, segmentSize);
} else {
if (storageFile.fileChannel.size() == 0L) {
throw new IOException("file channel is empty");
}
storageFile.readHeader();
storageFile.memoryMapBlobIndexes();
if (storageFile.version == 0) {
storageFile = migrateV0(path, blobCount, segmentSize, options, attrs, storageFile);
} else {
storageFile.readUsedSegments();
}
}
return storageFile;
}
}
private static IndexedStorageFile migrateV0(Path path, int blobCount, int segmentSize, Set<? extends OpenOption> options, FileAttribute<?>[] attrs, IndexedStorageFile storageFile) throws IOException {
storageFile.close();
Path tempFile = path.resolveSibling(path.getFileName().toString() + ".old");
Path tempPath = Files.move(path, tempFile, StandardCopyOption.REPLACE_EXISTING);
HashSet<OpenOption> newOptions = new HashSet(options);
newOptions.add(StandardOpenOption.CREATE);
storageFile = new IndexedStorageFile(path, FileChannel.open(path, newOptions, attrs));
storageFile.create(blobCount, segmentSize);
try {
IndexedStorageFile_v0 oldStorageFile = new IndexedStorageFile_v0(tempPath, FileChannel.open(tempPath, options, attrs));
try {
oldStorageFile.open();
for(int blobIndex = 0; blobIndex < blobCount; ++blobIndex) {
ByteBuffer blob = oldStorageFile.readBlob(blobIndex);
if (blob != null) {
storageFile.writeBlob(blobIndex, blob);
}
}
} catch (Throwable var17) {
try {
oldStorageFile.close();
} catch (Throwable var16) {
var17.addSuppressed(var16);
}
throw var17;
}
oldStorageFile.close();
} finally {
Files.delete(tempFile);
}
return storageFile;
}
private IndexedStorageFile(@Nonnull Path path, @Nonnull FileChannel fileChannel) {
this.segmentLocks = EMPTY_STAMPED_LOCKS;
this.usedSegmentsLock = new StampedLock();
this.usedSegments = new BitSet();
this.path = path;
this.fileChannel = fileChannel;
}
@Nonnull
public Path getPath() {
return this.path;
}
public int getBlobCount() {
return this.blobCount;
}
public int getSegmentSize() {
return this.segmentSize;
}
public int getCompressionLevel() {
return this.compressionLevel;
}
public void setFlushOnWrite(boolean flushOnWrite) {
this.flushOnWrite = flushOnWrite;
}
public void setCompressionLevel(int compressionLevel) {
this.compressionLevel = compressionLevel;
}
@Nonnull
protected IndexedStorageFile create(int blobCount, int segmentSize) throws IOException {
if (blobCount <= 0) {
throw new IllegalArgumentException("blobCount must be > 0");
} else if (segmentSize <= 0) {
throw new IllegalArgumentException("segmentSize must be > 0");
} else {
this.blobCount = blobCount;
this.segmentSize = segmentSize;
if (this.fileChannel.size() != 0L) {
throw new IOException("file channel is not empty");
} else {
this.writeHeader(blobCount, segmentSize);
this.memoryMapBlobIndexes();
return this;
}
}
}
protected void writeHeader(int blobCount, int segmentSize) throws IOException {
ByteBuffer header = getTempBuffer(HEADER_LENGTH);
header.put(MAGIC_BYTES);
header.putInt(VERSION_OFFSET, 1);
header.putInt(BLOB_COUNT_OFFSET, blobCount);
header.putInt(SEGMENT_SIZE_OFFSET, segmentSize);
header.position(0);
if (this.fileChannel.write(header, 0L) != HEADER_LENGTH) {
throw new IllegalStateException();
}
}
protected void readHeader() throws IOException {
ByteBuffer header = getTempBuffer(HEADER_LENGTH);
if (this.fileChannel.read(header, 0L) != HEADER_LENGTH) {
throw new IllegalStateException();
} else {
header.position(0);
header.limit(20);
if (!MAGIC_BUFFER.equals(header)) {
header.position(0);
byte[] dst = new byte[20];
header.get(dst);
String var10002 = String.valueOf(header);
throw new IOException("Invalid MAGIC! " + var10002 + ", " + Arrays.toString(dst) + " expected " + Arrays.toString(MAGIC_BYTES));
} else {
header.limit(HEADER_LENGTH);
this.version = header.getInt(VERSION_OFFSET);
if (this.version >= 0 && this.version <= 1) {
this.blobCount = header.getInt(BLOB_COUNT_OFFSET);
this.segmentSize = header.getInt(SEGMENT_SIZE_OFFSET);
} else {
throw new IOException("Invalid version! " + this.version);
}
}
}
}
protected void memoryMapBlobIndexes() throws IOException {
this.indexLocks = new StampedLock[this.blobCount];
for(int i = 0; i < this.blobCount; ++i) {
this.indexLocks[i] = new StampedLock();
}
this.mappedBlobIndexes = this.fileChannel.map(MapMode.READ_WRITE, (long)HEADER_LENGTH, (long)this.blobCount * 4L);
}
protected void readUsedSegments() throws IOException {
long stamp = this.usedSegmentsLock.writeLock();
try {
for(int blobIndex = 0; blobIndex < this.blobCount; ++blobIndex) {
int indexPos = blobIndex * 4;
long segmentStamp = this.indexLocks[blobIndex].readLock();
int firstSegmentIndex;
int compressedLength;
try {
firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos);
if (firstSegmentIndex == 0) {
compressedLength = 0;
} else {
ByteBuffer blobHeaderBuffer = this.readBlobHeader(firstSegmentIndex);
compressedLength = blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET);
}
} finally {
this.indexLocks[blobIndex].unlockRead(segmentStamp);
}
if (compressedLength > 0) {
int segmentsCount = this.requiredSegments((long)(BLOB_HEADER_LENGTH + compressedLength));
this.usedSegments.set(firstSegmentIndex, firstSegmentIndex + segmentsCount);
}
}
} finally {
this.usedSegmentsLock.unlockWrite(stamp);
}
}
public long size() throws IOException {
return this.fileChannel.size();
}
public int segmentSize() {
try {
return this.requiredSegments(this.fileChannel.size() - this.segmentsBase()) + 1;
} catch (IOException var2) {
return -1;
}
}
public int segmentCount() {
long stamp = this.usedSegmentsLock.tryOptimisticRead();
int count = this.usedSegments.cardinality();
if (this.usedSegmentsLock.validate(stamp)) {
return count;
} else {
stamp = this.usedSegmentsLock.readLock();
int var4;
try {
var4 = this.usedSegments.cardinality();
} finally {
this.usedSegmentsLock.unlockRead(stamp);
}
return var4;
}
}
@Nonnull
public IntList keys() {
IntArrayList list = new IntArrayList(this.blobCount);
for(int blobIndex = 0; blobIndex < this.blobCount; ++blobIndex) {
int indexPos = blobIndex * 4;
StampedLock lock = this.indexLocks[blobIndex];
long stamp = lock.tryOptimisticRead();
int segmentIndex = this.mappedBlobIndexes.getInt(indexPos);
if (lock.validate(stamp)) {
if (segmentIndex != 0) {
list.add(blobIndex);
}
} else {
stamp = lock.readLock();
try {
if (this.mappedBlobIndexes.getInt(indexPos) != 0) {
list.add(blobIndex);
}
} finally {
lock.unlockRead(stamp);
}
}
}
return list;
}
public int readBlobLength(int blobIndex) throws IOException {
if (blobIndex >= 0 && blobIndex < this.blobCount) {
int indexPos = blobIndex * 4;
long stamp = this.indexLocks[blobIndex].readLock();
byte var6;
try {
int firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos);
if (firstSegmentIndex != 0) {
ByteBuffer blobHeaderBuffer = this.readBlobHeader(firstSegmentIndex);
int var7 = blobHeaderBuffer.getInt(SRC_LENGTH_OFFSET);
return var7;
}
var6 = 0;
} finally {
this.indexLocks[blobIndex].unlockRead(stamp);
}
return var6;
} else {
throw new IndexOutOfBoundsException("Index out of range: " + blobIndex + " blobCount: " + this.blobCount);
}
}
public int readBlobCompressedLength(int blobIndex) throws IOException {
if (blobIndex >= 0 && blobIndex < this.blobCount) {
int indexPos = blobIndex * 4;
long stamp = this.indexLocks[blobIndex].readLock();
byte var6;
try {
int firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos);
if (firstSegmentIndex != 0) {
ByteBuffer blobHeaderBuffer = this.readBlobHeader(firstSegmentIndex);
int var7 = blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET);
return var7;
}
var6 = 0;
} finally {
this.indexLocks[blobIndex].unlockRead(stamp);
}
return var6;
} else {
throw new IndexOutOfBoundsException("Index out of range: " + blobIndex + " blobCount: " + this.blobCount);
}
}
@Nullable
public ByteBuffer readBlob(int blobIndex) throws IOException {
if (blobIndex >= 0 && blobIndex < this.blobCount) {
int indexPos = blobIndex * 4;
long stamp = this.indexLocks[blobIndex].readLock();
ByteBuffer src;
int srcLength;
label53: {
ByteBuffer blobHeaderBuffer;
try {
int firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos);
if (firstSegmentIndex != 0) {
blobHeaderBuffer = this.readBlobHeader(firstSegmentIndex);
srcLength = blobHeaderBuffer.getInt(SRC_LENGTH_OFFSET);
int compressedLength = blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET);
src = this.readSegments(firstSegmentIndex, compressedLength);
break label53;
}
blobHeaderBuffer = null;
} finally {
this.indexLocks[blobIndex].unlockRead(stamp);
}
return blobHeaderBuffer;
}
src.position(0);
return Zstd.decompress(src, srcLength);
} else {
throw new IndexOutOfBoundsException("Index out of range: " + blobIndex + " blobCount: " + this.blobCount);
}
}
public void readBlob(int blobIndex, @Nonnull ByteBuffer dest) throws IOException {
if (blobIndex >= 0 && blobIndex < this.blobCount) {
int indexPos = blobIndex * 4;
long stamp = this.indexLocks[blobIndex].readLock();
ByteBuffer src;
int srcLength;
try {
int firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos);
if (firstSegmentIndex == 0) {
return;
}
ByteBuffer blobHeaderBuffer = this.readBlobHeader(firstSegmentIndex);
srcLength = blobHeaderBuffer.getInt(SRC_LENGTH_OFFSET);
int compressedLength = blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET);
if (srcLength > dest.remaining()) {
throw new IllegalArgumentException("dest buffer is not large enough! required dest.remaining() >= " + srcLength);
}
src = this.readSegments(firstSegmentIndex, compressedLength);
} finally {
this.indexLocks[blobIndex].unlockRead(stamp);
}
src.position(0);
if (dest.isDirect()) {
Zstd.decompress(dest, src);
} else {
ByteBuffer tempDest = allocateDirect(srcLength);
try {
Zstd.decompress(tempDest, src);
tempDest.position(0);
dest.put(tempDest);
} finally {
if (UnsafeUtil.UNSAFE != null) {
UnsafeUtil.UNSAFE.invokeCleaner(tempDest);
}
}
}
} else {
throw new IndexOutOfBoundsException("Index out of range: " + blobIndex + " blobCount: " + this.blobCount);
}
}
@Nonnull
protected ByteBuffer readBlobHeader(int firstSegmentIndex) throws IOException {
if (firstSegmentIndex == 0) {
throw new IllegalArgumentException("Invalid segment index!");
} else {
ByteBuffer blobHeaderBuffer = getTempBuffer(BLOB_HEADER_LENGTH);
if (this.fileChannel.read(blobHeaderBuffer, this.segmentPosition(firstSegmentIndex)) != BLOB_HEADER_LENGTH) {
throw new IllegalStateException();
} else {
return blobHeaderBuffer;
}
}
}
@Nonnull
protected ByteBuffer readSegments(int firstSegmentIndex, int compressedLength) throws IOException {
ByteBuffer buffer = allocateDirect(compressedLength);
long segmentPosition = this.segmentPosition(firstSegmentIndex);
if (this.fileChannel.read(buffer, segmentPosition + (long)BLOB_HEADER_LENGTH) != compressedLength) {
throw new IllegalStateException();
} else if (buffer.remaining() != 0) {
throw new IOException("Failed to read segments: " + firstSegmentIndex + ", " + compressedLength + ", " + String.valueOf(buffer));
} else {
return buffer;
}
}
public void writeBlob(int blobIndex, @Nonnull ByteBuffer src) throws IOException {
if (blobIndex >= 0 && blobIndex < this.blobCount) {
int srcLength = src.remaining();
int maxCompressedLength = (int)Zstd.compressBound((long)srcLength);
ByteBuffer dest = allocateDirect(BLOB_HEADER_LENGTH + maxCompressedLength);
dest.putInt(SRC_LENGTH_OFFSET, srcLength);
dest.position(BLOB_HEADER_LENGTH);
int compressedLength;
if (src.isDirect()) {
compressedLength = Zstd.compress(dest, src, this.compressionLevel);
} else {
ByteBuffer tempSrc = allocateDirect(srcLength);
try {
tempSrc.put(src);
tempSrc.position(0);
compressedLength = Zstd.compress(dest, tempSrc, this.compressionLevel);
} finally {
if (UnsafeUtil.UNSAFE != null) {
UnsafeUtil.UNSAFE.invokeCleaner(tempSrc);
}
}
}
dest.putInt(COMPRESSED_LENGTH_OFFSET, compressedLength);
dest.limit(dest.position());
dest.position(0);
int indexPos = blobIndex * 4;
long stamp = this.indexLocks[blobIndex].writeLock();
try {
int oldSegmentLength = 0;
int oldFirstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos);
if (oldFirstSegmentIndex != 0) {
ByteBuffer blobHeaderBuffer = this.readBlobHeader(oldFirstSegmentIndex);
int oldCompressedLength = blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET);
oldSegmentLength = this.requiredSegments((long)(BLOB_HEADER_LENGTH + oldCompressedLength));
}
int firstSegmentIndex = this.writeSegments(dest);
if (this.flushOnWrite) {
this.fileChannel.force(false);
}
this.mappedBlobIndexes.putInt(indexPos, firstSegmentIndex);
if (this.flushOnWrite) {
this.mappedBlobIndexes.force(indexPos, 4);
}
if (oldSegmentLength > 0) {
long usedSegmentsStamp = this.usedSegmentsLock.writeLock();
try {
this.usedSegments.clear(oldFirstSegmentIndex, oldFirstSegmentIndex + oldSegmentLength);
} finally {
this.usedSegmentsLock.unlockWrite(usedSegmentsStamp);
}
}
} finally {
this.indexLocks[blobIndex].unlockWrite(stamp);
}
} else {
throw new IndexOutOfBoundsException("Index out of range: " + blobIndex + " blobCount: " + this.blobCount);
}
}
public void removeBlob(int blobIndex) throws IOException {
if (blobIndex >= 0 && blobIndex < this.blobCount) {
int indexPos = blobIndex * 4;
long stamp = this.indexLocks[blobIndex].writeLock();
try {
int oldFirstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos);
if (oldFirstSegmentIndex != 0) {
ByteBuffer blobHeaderBuffer = this.readBlobHeader(oldFirstSegmentIndex);
int oldCompressedLength = blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET);
int oldSegmentLength = this.requiredSegments((long)(BLOB_HEADER_LENGTH + oldCompressedLength));
this.mappedBlobIndexes.putInt(indexPos, 0);
if (this.flushOnWrite) {
this.mappedBlobIndexes.force(indexPos, 4);
}
long usedSegmentsStamp = this.usedSegmentsLock.writeLock();
try {
this.usedSegments.clear(oldFirstSegmentIndex, oldFirstSegmentIndex + oldSegmentLength);
} finally {
this.usedSegmentsLock.unlockWrite(usedSegmentsStamp);
}
}
} finally {
this.indexLocks[blobIndex].unlockWrite(stamp);
}
} else {
throw new IndexOutOfBoundsException("Index out of range: " + blobIndex + " blobCount: " + this.blobCount);
}
}
protected int writeSegments(@Nonnull ByteBuffer data) throws IOException {
int dataRemaining = data.remaining();
int segmentsCount = this.requiredSegments((long)dataRemaining);
SegmentRangeWriteLock segmentLock = this.findFreeSegment(segmentsCount);
int var8;
try {
int firstSegmentIndex = segmentLock.segmentIndex;
if (this.fileChannel.write(data, this.segmentPosition(firstSegmentIndex)) != dataRemaining) {
throw new IllegalStateException();
}
long stamp = this.usedSegmentsLock.writeLock();
try {
this.usedSegments.set(firstSegmentIndex, firstSegmentIndex + segmentsCount);
} finally {
this.usedSegmentsLock.unlockWrite(stamp);
}
var8 = firstSegmentIndex;
} finally {
segmentLock.unlock();
}
return var8;
}
@Nonnull
private SegmentRangeWriteLock findFreeSegment(int count) {
long[] stamps = new long[count];
int index = 1;
label120:
while(true) {
long indexesStamp = this.usedSegmentsLock.readLock();
try {
int start = 0;
int found = 0;
while(found < count) {
int nextUsedIndex = this.usedSegments.nextSetBit(index);
if (nextUsedIndex < 0) {
start = index;
break;
}
if (index == nextUsedIndex) {
start = this.usedSegments.nextClearBit(index);
nextUsedIndex = this.usedSegments.nextSetBit(start + 1);
if (nextUsedIndex < 0) {
break;
}
found = nextUsedIndex - start;
index = nextUsedIndex + 1;
} else {
start = index;
found = nextUsedIndex - index;
index = nextUsedIndex + 1;
}
}
for(int i = count - 1; i >= 0; --i) {
stamps[i] = this.getSegmentLock(start + i).tryWriteLock();
if (stamps[i] == 0L) {
for(int j = count - 1; j > i; --j) {
this.getSegmentLock(start + j).unlockWrite(stamps[j]);
}
index = start + i + 1;
continue label120;
}
}
SegmentRangeWriteLock var15 = new SegmentRangeWriteLock(start, count, stamps);
return var15;
} finally {
this.usedSegmentsLock.unlockRead(indexesStamp);
}
}
}
protected StampedLock getSegmentLock(int segmentIndex) {
if (segmentIndex < this.segmentLocks.length) {
return this.segmentLocks[segmentIndex];
} else {
long stamp = this.segmentLocksLock.writeLock();
StampedLock var4;
try {
if (segmentIndex >= this.segmentLocks.length) {
int newLength = segmentIndex + 1;
StampedLock[] newArray = (StampedLock[])Arrays.copyOf(this.segmentLocks, newLength);
for(int i = this.segmentLocks.length; i < newLength; ++i) {
newArray[i] = new StampedLock();
}
this.segmentLocks = newArray;
StampedLock var11 = this.segmentLocks[segmentIndex];
return var11;
}
var4 = this.segmentLocks[segmentIndex];
} finally {
this.segmentLocksLock.unlockWrite(stamp);
}
return var4;
}
}
protected long segmentsBase() {
return (long)HEADER_LENGTH + (long)this.blobCount * 4L;
}
protected long segmentOffset(int segmentIndex) {
if (segmentIndex == 0) {
throw new IllegalArgumentException("Invalid segment index!");
} else {
return (long)(segmentIndex - 1) * (long)this.segmentSize;
}
}
protected long segmentPosition(int segmentIndex) {
return this.segmentOffset(segmentIndex) + this.segmentsBase();
}
protected int positionToSegment(long position) {
long segmentOffset = position - this.segmentsBase();
if (segmentOffset < 0L) {
throw new IllegalArgumentException("position is before the segments start");
} else {
return (int)(segmentOffset / (long)this.segmentSize) + 1;
}
}
protected int requiredSegments(long dataLength) {
return (int)((dataLength + (long)this.segmentSize - 1L) / (long)this.segmentSize);
}
public FileLock lock() throws IOException {
return this.fileChannel.lock();
}
public void force(boolean metaData) throws IOException {
this.fileChannel.force(metaData);
this.mappedBlobIndexes.force();
}
public void close() throws IOException {
this.fileChannel.close();
if (UnsafeUtil.UNSAFE != null) {
UnsafeUtil.UNSAFE.invokeCleaner(this.mappedBlobIndexes);
}
this.mappedBlobIndexes = null;
}
@Nonnull
public String toString() {
String var10000 = String.valueOf(this.fileChannel);
return "IndexedStorageFile{fileChannel=" + var10000 + ", compressionLevel=" + this.compressionLevel + ", blobCount=" + this.blobCount + ", segmentSize=" + this.segmentSize + ", mappedBlobIndexes=" + String.valueOf(this.mappedBlobIndexes) + ", usedSegments=" + String.valueOf(this.usedSegments) + "}";
}
static {
METRICS_REGISTRY = (new MetricsRegistry()).register("Size", (file) -> {
try {
return file.size();
} catch (IOException var2) {
return -1L;
}
}, Codec.LONG).register("CompressionLevel", (file) -> file.getCompressionLevel(), Codec.INTEGER).register("BlobCount", (file) -> file.getBlobCount(), Codec.INTEGER).register("UsedBlobCount", (file) -> file.keys().size(), Codec.INTEGER).register("SegmentSize", (file) -> file.segmentSize(), Codec.INTEGER).register("SegmentCount", (file) -> file.segmentCount(), Codec.INTEGER);
HOH = new OffsetHelper();
MAGIC_OFFSET = HOH.next(20);
VERSION_OFFSET = HOH.next(4);
BLOB_COUNT_OFFSET = HOH.next(4);
SEGMENT_SIZE_OFFSET = HOH.next(4);
HEADER_LENGTH = HOH.length();
BOH = new OffsetHelper();
SRC_LENGTH_OFFSET = BOH.next(4);
COMPRESSED_LENGTH_OFFSET = BOH.next(4);
BLOB_HEADER_LENGTH = BOH.length();
NO_ATTRIBUTES = new FileAttribute[0];
MAGIC_BYTES = "HytaleIndexedStorage".getBytes(StandardCharsets.UTF_8);
MAGIC_BUFFER = ByteBuffer.wrap(MAGIC_BYTES);
MAGIC_BUFFER.position(0);
CACHED_TEMP_BUFFER = ThreadLocal.withInitial(() -> ByteBuffer.allocateDirect(HEADER_LENGTH));
}
static class OffsetHelper {
private int index;
OffsetHelper() {
}
public int next(int len) {
int cur = this.index;
this.index += len;
return cur;
}
public int length() {
return this.index;
}
}
protected class SegmentRangeWriteLock {
private final int segmentIndex;
private final int count;
private final long[] stamps;
public SegmentRangeWriteLock(int segmentIndex, int count, long[] stamps) {
if (segmentIndex == 0) {
throw new IllegalArgumentException("Invalid segment index!");
} else if (count == 0) {
throw new IllegalArgumentException("Invalid count!");
} else {
this.segmentIndex = segmentIndex;
this.count = count;
this.stamps = stamps;
}
}
protected void unlock() {
for(int i = 0; i < this.count; ++i) {
IndexedStorageFile.this.getSegmentLock(this.segmentIndex + i).unlockWrite(this.stamps[i]);
this.stamps[i] = 0L;
}
}
}
}
com/hypixel/hytale/storage/IndexedStorageFile_v0.java
package com.hypixel.hytale.storage;
import com.github.luben.zstd.Zstd;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.metrics.MetricsRegistry;
import com.hypixel.hytale.unsafe.UnsafeUtil;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.StandardCharsets;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.locks.StampedLock;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@Deprecated
public class IndexedStorageFile_v0 implements Closeable {
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
StampedLock[] EMPTY_STAMPED_LOCKS = [];
MetricsRegistry<IndexedStorageFile_v0> METRICS_REGISTRY;
;
;
;
;
;
;
OffsetHelper HOH;
;
MAGIC_OFFSET;
VERSION_OFFSET;
BLOB_COUNT_OFFSET;
SEGMENT_SIZE_OFFSET;
HEADER_LENGTH;
OffsetHelper SOH;
NEXT_SEGMENT_OFFSET;
SEGMENT_HEADER_LENGTH;
OffsetHelper BOH;
SRC_LENGTH_OFFSET;
COMPRESSED_LENGTH_OFFSET;
BLOB_HEADER_LENGTH;
;
;
-;
;
FileAttribute<?>[] NO_ATTRIBUTES;
[] MAGIC_BYTES;
ByteBuffer MAGIC_BUFFER;
ThreadLocal<ByteBuffer> CACHED_TEMP_BUFFER;
Path path;
FileChannel fileChannel;
;
;
blobCount;
segmentSize;
StampedLock[] indexLocks;
MappedByteBuffer mappedBlobIndexes;
();
StampedLock[] segmentLocks;
StampedLock nextSegmentIndexesLock;
[] nextSegmentIndexes;
ByteBuffer {
(ByteBuffer)CACHED_TEMP_BUFFER.get();
buffer.position();
buffer.limit(length);
buffer;
}
ByteBuffer {
ByteBuffer.allocateDirect(length);
}
IndexedStorageFile_v0 IOException {
open(path, , , Set.of(options), NO_ATTRIBUTES);
}
IndexedStorageFile_v0 IOException {
open(path, , , options, attrs);
}
IndexedStorageFile_v0 IOException {
open(path, blobCount, segmentSize, Set.of(options), NO_ATTRIBUTES);
}
IndexedStorageFile_v0 IOException {
(path, options, attrs);
(options.contains(StandardOpenOption.CREATE_NEW)) {
storageFile.create(blobCount, segmentSize);
storageFile;
} {
(options.contains(StandardOpenOption.CREATE) && storageFile.fileChannel.size() == ) {
storageFile.create(blobCount, segmentSize);
} {
storageFile.open();
}
storageFile;
}
}
IOException {
.segmentLocks = EMPTY_STAMPED_LOCKS;
.nextSegmentIndexesLock = ();
.nextSegmentIndexes = [];
.path = path;
.fileChannel = FileChannel.open(path, options, attrs);
}
IndexedStorageFile_v0( Path path, FileChannel fileChannel) IOException {
.segmentLocks = EMPTY_STAMPED_LOCKS;
.nextSegmentIndexesLock = ();
.nextSegmentIndexes = [];
.path = path;
.fileChannel = fileChannel;
}
Path {
.path;
}
{
.blobCount;
}
{
.segmentSize;
}
{
.compressionLevel;
}
{
.compressionLevel = compressionLevel;
}
{
.contiguousBlobs;
}
{
.contiguousBlobs = contiguousBlobs;
}
IndexedStorageFile_v0 IOException {
(blobCount <= ) {
();
} (segmentSize <= ) {
();
} {
.blobCount = blobCount;
.segmentSize = segmentSize;
(.fileChannel.size() != ) {
();
} {
.writeHeader(blobCount, segmentSize);
.memoryMapBlobIndexes();
;
}
}
}
IOException {
getTempBuffer(HEADER_LENGTH);
header.put(MAGIC_BYTES);
header.putInt(VERSION_OFFSET, );
header.putInt(BLOB_COUNT_OFFSET, blobCount);
header.putInt(SEGMENT_SIZE_OFFSET, segmentSize);
header.position();
(.fileChannel.write(header, ) != HEADER_LENGTH) {
();
}
}
IndexedStorageFile_v0 IOException {
(.fileChannel.size() == ) {
();
} {
.readHeader();
.memoryMapBlobIndexes();
.readNextIndexes();
.processTempIndexes();
;
}
}
IOException {
getTempBuffer(HEADER_LENGTH);
(.fileChannel.read(header, ) != HEADER_LENGTH) {
();
} {
header.position();
header.limit();
(!MAGIC_BUFFER.equals(header)) {
header.position();
[] dst = [];
header.get(dst);
String.valueOf(header);
( + var10002 + + Arrays.toString(dst) + + Arrays.toString(MAGIC_BYTES));
} {
header.limit(HEADER_LENGTH);
header.getInt(VERSION_OFFSET);
(version >= && version <= ) {
.blobCount = header.getInt(BLOB_COUNT_OFFSET);
.segmentSize = header.getInt(SEGMENT_SIZE_OFFSET);
} {
( + version);
}
}
}
}
IOException {
.indexLocks = [.blobCount];
( ; i < .blobCount; ++i) {
.indexLocks[i] = ();
}
.indexesLength() * ;
.mappedBlobIndexes = .fileChannel.map(MapMode.READ_WRITE, ()HEADER_LENGTH, ()indexesSize);
}
IOException {
getTempBuffer();
.nextSegmentIndexesLock.writeLock();
{
.nextSegmentIndexes = [.requiredSegments(.fileChannel.size() - .segmentsBase()) + ];
( ; segmentIndex < .nextSegmentIndexes.length; ++segmentIndex) {
tempIndexBuffer.position();
(.fileChannel.read(tempIndexBuffer, .segmentPosition(segmentIndex)) != ) {
tempIndexBuffer.position();
tempIndexBuffer.putInt(, );
.fileChannel.write(tempIndexBuffer, .segmentPosition(segmentIndex));
;
}
.nextSegmentIndexes[segmentIndex] = tempIndexBuffer.getInt(NEXT_SEGMENT_OFFSET);
}
} {
.nextSegmentIndexesLock.unlockWrite(stamp);
}
}
IOException {
;
;
getTempBuffer();
.indexesLength();
( ; blobIndex < .blobCount; ++blobIndex) {
indexesLength + blobIndex * ;
.mappedBlobIndexes.getInt(tempIndexPos);
(firstSegmentIndex != ) {
++blobsCleared;
segmentsCleared += .clearSegments(firstSegmentIndex, tempIndexBuffer);
.mappedBlobIndexes.putInt(tempIndexPos, );
}
}
(blobsCleared != || segmentsCleared != ) {
LOGGER.at(Level.WARNING).log(, .fileChannel, blobsCleared, segmentsCleared);
}
}
IOException {
[] segments = [];
;
segmentIndex;
( firstSegmentIndex; nextSegmentIndex != && nextSegmentIndex != -; nextSegmentIndex = segmentIndex) {
(count >= segments.length) {
segments = Arrays.copyOf(segments, count * );
}
segments[count] = nextSegmentIndex;
++count;
.nextSegmentIndexesLock.tryOptimisticRead();
segmentIndex = .nextSegmentIndexes[nextSegmentIndex];
(!.nextSegmentIndexesLock.validate(indexesStamp)) {
indexesStamp = .nextSegmentIndexesLock.readLock();
{
segmentIndex = .nextSegmentIndexes[nextSegmentIndex];
} {
.nextSegmentIndexesLock.unlockRead(indexesStamp);
}
}
}
tempIndexBuffer.putInt(, );
( count - ; i >= ; --i) {
tempIndexBuffer.position();
segments[i];
.getSegmentLock(segmentIndex);
segmentLock.writeLock();
{
(.fileChannel.write(tempIndexBuffer, .segmentPosition(segmentIndex)) != ) {
();
}
.nextSegmentIndexesLock.writeLock();
{
.nextSegmentIndexes[segmentIndex] = ;
} {
.nextSegmentIndexesLock.unlockWrite(indexesStamp);
}
} {
segmentLock.unlockWrite(segmentStamp);
}
}
count;
}
IOException {
.fileChannel.size();
}
{
.nextSegmentIndexesLock.tryOptimisticRead();
.nextSegmentIndexes.length;
(.nextSegmentIndexesLock.validate(stamp)) {
value;
} {
stamp = .nextSegmentIndexesLock.readLock();
var4;
{
var4 = .nextSegmentIndexes.length;
} {
.nextSegmentIndexesLock.unlockRead(stamp);
}
var4;
}
}
{
.nextSegmentIndexesLock.tryOptimisticRead();
;
[] temp = .nextSegmentIndexes;
( ; i < temp.length; ++i) {
(temp[i] != ) {
++count;
}
}
(.nextSegmentIndexesLock.validate(stamp)) {
count;
} {
stamp = .nextSegmentIndexesLock.readLock();
var13;
{
count = ;
temp = .nextSegmentIndexes;
( ; i < temp.length; ++i) {
(temp[i] != ) {
++count;
}
}
var13 = count;
} {
.nextSegmentIndexesLock.unlockRead(stamp);
}
var13;
}
}
IntList {
(.blobCount);
( ; blobIndex < .blobCount; ++blobIndex) {
blobIndex * ;
.indexLocks[blobIndex];
lock.tryOptimisticRead();
.mappedBlobIndexes.getInt(indexPos);
(lock.validate(stamp)) {
(segmentIndex != ) {
list.add(blobIndex);
}
} {
stamp = lock.readLock();
{
(.mappedBlobIndexes.getInt(indexPos) != ) {
list.add(blobIndex);
}
} {
lock.unlockRead(stamp);
}
}
}
list;
}
IOException {
(blobIndex >= && blobIndex < .blobCount) {
blobIndex * ;
.indexLocks[blobIndex].readLock();
var6;
{
.mappedBlobIndexes.getInt(indexPos);
(firstSegmentIndex != ) {
.readBlobHeader(firstSegmentIndex);
blobHeaderBuffer.getInt(SRC_LENGTH_OFFSET);
var7;
}
var6 = ;
} {
.indexLocks[blobIndex].unlockRead(stamp);
}
var6;
} {
( + blobIndex + + .blobCount);
}
}
IOException {
(blobIndex >= && blobIndex < .blobCount) {
blobIndex * ;
.indexLocks[blobIndex].readLock();
var6;
{
.mappedBlobIndexes.getInt(indexPos);
(firstSegmentIndex != ) {
.readBlobHeader(firstSegmentIndex);
blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET);
var7;
}
var6 = ;
} {
.indexLocks[blobIndex].unlockRead(stamp);
}
var6;
} {
( + blobIndex + + .blobCount);
}
}
ByteBuffer IOException {
(blobIndex >= && blobIndex < .blobCount) {
blobIndex * ;
.indexLocks[blobIndex].readLock();
ByteBuffer src;
srcLength;
label53: {
ByteBuffer blobHeaderBuffer;
{
.mappedBlobIndexes.getInt(indexPos);
(firstSegmentIndex != ) {
blobHeaderBuffer = .readBlobHeader(firstSegmentIndex);
srcLength = blobHeaderBuffer.getInt(SRC_LENGTH_OFFSET);
blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET);
src = .readSegments(firstSegmentIndex, compressedLength, blobHeaderBuffer);
label53;
}
blobHeaderBuffer = ;
} {
.indexLocks[blobIndex].unlockRead(stamp);
}
blobHeaderBuffer;
}
src.position();
Zstd.decompress(src, srcLength);
} {
( + blobIndex + + .blobCount);
}
}
IOException {
(blobIndex >= && blobIndex < .blobCount) {
blobIndex * ;
.indexLocks[blobIndex].readLock();
ByteBuffer src;
srcLength;
{
.mappedBlobIndexes.getInt(indexPos);
(firstSegmentIndex == ) {
;
}
.readBlobHeader(firstSegmentIndex);
srcLength = blobHeaderBuffer.getInt(SRC_LENGTH_OFFSET);
blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET);
(srcLength > dest.remaining()) {
( + srcLength);
}
src = .readSegments(firstSegmentIndex, compressedLength, blobHeaderBuffer);
} {
.indexLocks[blobIndex].unlockRead(stamp);
}
src.position();
(dest.isDirect()) {
Zstd.decompress(dest, src);
} {
allocateDirect(srcLength);
Zstd.decompress(tempDest, src);
tempDest.position();
dest.put(tempDest);
}
} {
( + blobIndex + + .blobCount);
}
}
ByteBuffer IOException {
(firstSegmentIndex == ) {
();
} {
getTempBuffer(BLOB_HEADER_LENGTH);
(.fileChannel.read(blobHeaderBuffer, .blobHeaderPosition(firstSegmentIndex)) != BLOB_HEADER_LENGTH) {
();
} {
blobHeaderBuffer;
}
}
}
ByteBuffer IOException {
tempHeaderBuffer.limit(SEGMENT_HEADER_LENGTH);
allocateDirect(compressedLength);
compressedLength;
firstSegmentIndex;
(nextSegmentIndex != && nextSegmentIndex != -) {
.segmentPosition(nextSegmentIndex);
.nextSegmentIndexesLock.tryOptimisticRead();
.nextSegmentIndexes[nextSegmentIndex];
(!.nextSegmentIndexesLock.validate(indexesStamp)) {
indexesStamp = .nextSegmentIndexesLock.readLock();
{
segmentIndex = .nextSegmentIndexes[nextSegmentIndex];
} {
.nextSegmentIndexesLock.unlockRead(indexesStamp);
}
}
nextSegmentIndex = segmentIndex;
SEGMENT_HEADER_LENGTH;
(buffer.position() == ) {
dataOffset += BLOB_HEADER_LENGTH;
}
Math.min(.segmentSize - dataOffset, remainingBytes);
buffer.limit(buffer.position() + dataToRead);
(.fileChannel.read(buffer, segmentPosition + ()dataOffset) != dataToRead) {
();
}
remainingBytes -= dataToRead;
(remainingBytes == ) {
;
}
}
(buffer.remaining() == && nextSegmentIndex == -) {
buffer;
} {
( + firstSegmentIndex + + compressedLength + + String.valueOf(buffer) + + nextSegmentIndex);
}
}
IOException {
(blobIndex >= && blobIndex < .blobCount) {
src.remaining();
()Zstd.compressBound(()srcLength);
allocateDirect(BLOB_HEADER_LENGTH + maxCompressedLength);
dest.putInt(SRC_LENGTH_OFFSET, srcLength);
dest.position(BLOB_HEADER_LENGTH);
compressedLength;
(src.isDirect()) {
compressedLength = Zstd.compress(dest, src, .compressionLevel);
} {
allocateDirect(srcLength);
tempSrc.put(src);
tempSrc.position();
compressedLength = Zstd.compress(dest, tempSrc, .compressionLevel);
}
dest.putInt(COMPRESSED_LENGTH_OFFSET, compressedLength);
dest.limit(dest.position());
dest.position();
blobIndex * ;
.indexesLength() + indexPos;
.indexLocks[blobIndex].writeLock();
{
.writeSegments(blobIndex, dest);
.mappedBlobIndexes.getInt(indexPos);
.mappedBlobIndexes.putInt(indexPos, firstSegmentIndex);
.mappedBlobIndexes.putInt(tempIndexPos, oldFirstSegmentIndex);
(oldFirstSegmentIndex != ) {
getTempBuffer();
.clearSegments(oldFirstSegmentIndex, tempIndexBuffer);
.mappedBlobIndexes.putInt(tempIndexPos, );
}
} {
.indexLocks[blobIndex].unlockWrite(stamp);
}
} {
( + blobIndex + + .blobCount);
}
}
IOException {
(blobIndex >= && blobIndex < .blobCount) {
blobIndex * ;
.indexesLength() + indexPos;
.indexLocks[blobIndex].writeLock();
{
.mappedBlobIndexes.getInt(indexPos);
(oldFirstSegmentIndex != ) {
.mappedBlobIndexes.putInt(indexPos, );
.mappedBlobIndexes.putInt(tempIndexPos, oldFirstSegmentIndex);
getTempBuffer();
.clearSegments(oldFirstSegmentIndex, tempIndexBuffer);
.mappedBlobIndexes.putInt(tempIndexPos, );
}
} {
.indexLocks[blobIndex].unlockWrite(stamp);
}
} {
( + blobIndex + + .blobCount);
}
}
IOException {
data.remaining();
blobIndex * ;
.indexesLength() + indexPos;
getTempBuffer();
(.contiguousBlobs) {
.requiredSegments(()dataRemaining);
.findFreeSegment(segmentsCount);
var34;
{
segmentLock.segmentIndex;
.mappedBlobIndexes.putInt(tempIndexPos, firstSegmentIndex);
firstSegmentIndex + segmentsCount;
.nextSegmentIndexesLock.writeLock();
{
(endSegmentIndex >= .nextSegmentIndexes.length) {
.nextSegmentIndexes = Arrays.copyOf(.nextSegmentIndexes, endSegmentIndex);
}
} {
.nextSegmentIndexesLock.unlockWrite(indexesResizeStamp);
}
( firstSegmentIndex; segmentIndex < endSegmentIndex; ++segmentIndex) {
.segmentPosition(segmentIndex);
segmentIndex + ;
next < endSegmentIndex ? next : -;
tempIndexBuffer.position();
tempIndexBuffer.putInt(, nextSegmentIndex);
(.fileChannel.write(tempIndexBuffer, segmentPosition) != SEGMENT_HEADER_LENGTH) {
();
}
.nextSegmentIndexesLock.writeLock();
{
.nextSegmentIndexes[segmentIndex] = nextSegmentIndex;
} {
.nextSegmentIndexesLock.unlockWrite(indexesStamp);
}
Math.min(.segmentSize - SEGMENT_HEADER_LENGTH, dataRemaining);
data.limit(data.position() + dataToWrite);
(.fileChannel.write(data, segmentPosition + ()SEGMENT_HEADER_LENGTH) != dataToWrite) {
();
}
dataRemaining -= dataToWrite;
}
var34 = firstSegmentIndex;
} {
segmentLock.unlockWrite();
}
var34;
} {
();
}
}
SegmentRangeLock {
;
;
;
() {
label95:
(found >= count) {
.tryWriteLockSegmentRange(start, count);
(segmentRangeLock == ) {
++start;
--found;
} {
( count - ; i >= ; --i) {
start + i;
.getNextIndex(segmentIndex);
(next != ) {
segmentRangeLock.unlockWrite();
index = segmentIndex + ;
start = ;
found = ;
label95;
}
}
segmentRangeLock;
}
}
.getSegmentLock(index);
segmentLock.tryReadLock();
(stamp == ) {
start = ;
found = ;
++index;
} {
next;
{
next = .getNextIndex(index);
} {
segmentLock.unlockRead(stamp);
}
(next == ) {
(start == ) {
start = index;
}
++found;
} {
start = ;
found = ;
}
++index;
}
}
}
{
.nextSegmentIndexesLock.tryOptimisticRead();
segmentIndex < .nextSegmentIndexes.length ? .nextSegmentIndexes[segmentIndex] : ;
(.nextSegmentIndexesLock.validate(indexesStamp)) {
nextSegmentIndex;
} {
indexesStamp = .nextSegmentIndexesLock.readLock();
var5;
{
var5 = segmentIndex < .nextSegmentIndexes.length ? .nextSegmentIndexes[segmentIndex] : ;
} {
.nextSegmentIndexesLock.unlockRead(indexesStamp);
}
var5;
}
}
StampedLock {
(segmentIndex < .segmentLocks.length) {
.segmentLocks[segmentIndex];
} {
.segmentLocksLock.writeLock();
StampedLock var4;
{
(segmentIndex >= .segmentLocks.length) {
segmentIndex + ;
StampedLock[] newArray = (StampedLock[])Arrays.copyOf(.segmentLocks, newLength);
( .segmentLocks.length; i < newLength; ++i) {
newArray[i] = ();
}
.segmentLocks = newArray;
.segmentLocks[segmentIndex];
var11;
}
var4 = .segmentLocks[segmentIndex];
} {
.segmentLocksLock.unlockWrite(stamp);
}
var4;
}
}
SegmentRangeLock {
[] stamps = [count];
( ; i < count; ++i) {
.getSegmentLock(start + i);
(segmentLock.isWriteLocked()) {
( ; i1 < i; ++i1) {
.getSegmentLock(start + i1).unlockWrite(stamps[i1]);
}
;
}
stamps[i] = segmentLock.writeLock();
}
(start, count, stamps);
}
{
.blobCount * ;
}
{
()HEADER_LENGTH + ().indexesLength() * ;
}
{
(segmentIndex == ) {
();
} {
()(segmentIndex - ) * ().segmentSize;
}
}
{
.segmentOffset(segmentIndex) + .segmentsBase();
}
{
position - .segmentsBase();
(segmentOffset < ) {
();
} {
()(segmentOffset / ().segmentSize) + ;
}
}
{
.segmentPosition(segmentIndex) + ()SEGMENT_HEADER_LENGTH;
}
{
.segmentSize - SEGMENT_HEADER_LENGTH;
()((dataLength + ()size - ) / ()size);
}
FileLock IOException {
.fileChannel.lock();
}
IOException {
.mappedBlobIndexes.force();
.fileChannel.force(metaData);
}
IOException {
.fileChannel.close();
(UnsafeUtil.UNSAFE != ) {
UnsafeUtil.UNSAFE.invokeCleaner(.mappedBlobIndexes);
}
.mappedBlobIndexes = ;
}
String {
String.valueOf(.fileChannel);
+ var10000 + + .compressionLevel + + .contiguousBlobs + + .blobCount + + .segmentSize + + String.valueOf(.mappedBlobIndexes) + + Arrays.toString(.nextSegmentIndexes) + ;
}
{
METRICS_REGISTRY = ( ()).register(, (file) -> {
{
file.size();
} (IOException var2) {
-;
}
}, Codec.LONG).register(, (file) -> file.getCompressionLevel(), Codec.INTEGER).register(, (file) -> file.isContiguousBlobs(), Codec.BOOLEAN).register(, (file) -> file.getBlobCount(), Codec.INTEGER).register(, (file) -> file.keys().size(), Codec.INTEGER).register(, (file) -> file.segmentSize(), Codec.INTEGER).register(, (file) -> file.segmentCount(), Codec.INTEGER);
HOH = ();
MAGIC_OFFSET = HOH.next();
VERSION_OFFSET = HOH.next();
BLOB_COUNT_OFFSET = HOH.next();
SEGMENT_SIZE_OFFSET = HOH.next();
HEADER_LENGTH = HOH.length();
SOH = ();
NEXT_SEGMENT_OFFSET = SOH.next();
SEGMENT_HEADER_LENGTH = SOH.length();
BOH = ();
SRC_LENGTH_OFFSET = BOH.next();
COMPRESSED_LENGTH_OFFSET = BOH.next();
BLOB_HEADER_LENGTH = BOH.length();
NO_ATTRIBUTES = [];
MAGIC_BYTES = .getBytes(StandardCharsets.UTF_8);
MAGIC_BUFFER = ByteBuffer.wrap(MAGIC_BYTES);
MAGIC_BUFFER.position();
CACHED_TEMP_BUFFER = ThreadLocal.withInitial(() -> ByteBuffer.allocateDirect(HEADER_LENGTH));
}
{
index;
OffsetHelper() {
}
{
.index;
.index += len;
cur;
}
{
.index;
}
}
{
segmentIndex;
count;
[] stamps;
{
.segmentIndex = segmentIndex;
.count = count;
.stamps = stamps;
}
{
( ; i < .count; ++i) {
IndexedStorageFile_v0..getSegmentLock(.segmentIndex + i).unlockRead(.stamps[i]);
.stamps[i] = ;
}
}
{
( ; i < .count; ++i) {
IndexedStorageFile_v0..getSegmentLock(.segmentIndex + i).unlockWrite(.stamps[i]);
.stamps[i] = ;
}
}
}
}
com/hypixel/hytale/storage/package-info.java
package com.hypixel.hytale.storage;