All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bt.torrent.messaging.ExchangedMetadata Maven / Gradle / Ivy

There is a newer version: 1.10
Show newest version
package bt.torrent.messaging;

import bt.data.BlockSet;
import bt.data.digest.SHA1Digester;
import bt.data.range.BlockRange;
import bt.data.range.ByteRange;
import bt.data.range.Range;
import bt.data.range.Ranges;

/**
 * BEP-9 torrent metadata, thread-safe
 */
class ExchangedMetadata {

    private volatile Range> metadata;
    private volatile BlockSet metadataBlocks;

    private volatile byte[] digest;
    private final Object digestLock;

    ExchangedMetadata(byte[] data, int blockSize) {
        BlockRange range = Ranges.blockRange(new ByteRange(data), blockSize);
        this.metadata = Ranges.synchronizedRange(range);
        this.metadataBlocks = Ranges.synchronizedBlockSet(range.getBlockSet());

        this.digestLock = new Object();
    }

    ExchangedMetadata(int totalSize, int blockSize) {
        this(new byte[totalSize], blockSize);
    }

    public boolean isBlockPresent(int blockIndex) {
        return metadataBlocks.isPresent(blockIndex);
    }

    public void setBlock(int blockIndex, byte[] block) {
        validateBlockIndex(blockIndex);
        metadata.getSubrange(blockIndex * metadataBlocks.blockSize()).putBytes(block);
    }

    public int getBlockCount() {
        return metadataBlocks.blockCount();
    }

    public boolean isComplete() {
        return metadataBlocks.isComplete();
    }

    /**
     * @throws IllegalStateException if metadata is not complete
     * @see #isComplete()
     */
    public byte[] getSha1Digest() {
        if (!metadataBlocks.isComplete()) {
            throw new IllegalStateException("Metadata is not complete");
        }

        if (digest == null) {
            synchronized (digestLock) {
                if (digest == null) {
                    digest = SHA1Digester.rolling(1000000).digest(metadata);
                }
            }
        }
        return digest;
    }

    /**
     * @throws IllegalStateException if metadata is not complete
     * @see #isComplete()
     */
    public byte[] getBytes() {
        if (!metadataBlocks.isComplete()) {
            throw new IllegalStateException("Metadata is not complete");
        }

        return metadata.getBytes();
    }

    public byte[] getBlock(int blockIndex) {
        validateBlockIndex(blockIndex);

        int blockLength;
        // last piece may be smaller than the rest
        if (blockIndex == metadataBlocks.blockCount() - 1) {
            blockLength = (int) metadataBlocks.lastBlockSize();
        } else {
            blockLength = (int) metadataBlocks.blockSize();
        }

        return metadata.getSubrange(blockIndex * metadataBlocks.blockSize(), blockLength).getBytes();
    }

    public int length() {
        return (int) metadata.length();
    }

    private void validateBlockIndex(int blockIndex) {
        int blockCount = metadataBlocks.blockCount();
        if (blockIndex < 0 || blockIndex >= blockCount) {
            throw new IllegalArgumentException("Invalid block index: " + blockIndex + "; expected 0.." + blockCount);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy