/* * FileSponge * Copyright (C) 2021 Andrea Cavalli * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.warp.filesponge; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.PooledByteBufAllocator; import io.netty.buffer.api.Buffer; import io.netty.buffer.api.BufferAllocator; import io.netty.buffer.api.Send; import it.cavallium.dbengine.database.serialization.BufferDataInput; import it.cavallium.dbengine.database.serialization.BufferDataOutput; import it.cavallium.dbengine.database.serialization.Serializer; import it.unimi.dsi.fastutil.booleans.BooleanArrayList; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import org.apache.commons.lang3.SerializationException; import org.jetbrains.annotations.NotNull; /** * size -1 = unknown size */ public record DiskMetadata(int size, BooleanArrayList downloadedBlocks) { public boolean isDownloadedFully() { boolean downloadedFullyVal; // Ensure blocks count is valid by calling getBlocksCount() getBlocksCount(); // It's fully downloaded if every block is true downloadedFullyVal = !this.downloadedBlocks().contains(false); return downloadedFullyVal; } @SuppressWarnings("UnusedReturnValue") private int getBlocksCount() { if (size == -1) { return downloadedBlocks().size(); } var expectedBlocksCount = getBlocksCount(size, FileSponge.BLOCK_SIZE); if (this.downloadedBlocks().size() != expectedBlocksCount) { throw new IllegalStateException( "Blocks array length (" + this.downloadedBlocks().size() + ") != expected blocks count (" + expectedBlocksCount + ")"); } return expectedBlocksCount; } public static int getBlocksCount(int size, int blockSize) { if (size == -1) { return 0; } return (size + (blockSize - size % blockSize)) / blockSize; } public Metadata asMetadata() { return new Metadata(size); } public boolean isDownloadedBlock(int id) { if (size == -1 && downloadedBlocks().size() <= id) { return false; } else { return downloadedBlocks().getBoolean(id); } } public static class DiskMetadataSerializer implements Serializer { private final BufferAllocator allocator; public DiskMetadataSerializer(BufferAllocator allocator) { this.allocator = allocator; } @Override public @NotNull DeserializationResult deserialize(@NotNull Send serialized) { var dis = new BufferDataInput(serialized); int size = dis.readInt(); int blocksCount; if (size == -1) { blocksCount = dis.readShort(); } else { blocksCount = getBlocksCount(size, FileSponge.BLOCK_SIZE); } var downloadedBlocks = new BooleanArrayList(blocksCount); for (int i = 0; i < blocksCount; i++) { downloadedBlocks.add(dis.readBoolean()); } return new DeserializationResult<>(new DiskMetadata(size, downloadedBlocks), dis.getReadBytesCount()); } @Override public @NotNull Send serialize(@NotNull DiskMetadata deserialized) { try (var buffer = allocator.allocate(64)) { var dos = new BufferDataOutput(buffer); dos.writeInt(deserialized.size()); if (deserialized.size == -1) { dos.writeShort(deserialized.getBlocksCount()); } else { deserialized.getBlocksCount(); } for (boolean downloadedBlock : deserialized.downloadedBlocks()) { dos.writeBoolean(downloadedBlock); } return buffer.send(); } } } }