/** * BitTorrent Protocol Structures (BEP 9 & 10) * This file defines the structures for the Extension Protocol and Metadata Extension. */ export interface ExtensionHandshake { m: { [extensionName: string]: number }; // Supported extensions and their local IDs p?: number; // Local TCP port v?: string; // Client version string metadata_size?: number; // Total size of the info dictionary in bytes } export interface MetadataRequest { msg_type: number; // 0 for request, 1 for data, 2 for reject piece: number; // The piece index being requested total_size?: number; // Only for data messages } /** * Simulates the reassembly of metadata pieces. * In a real scenario, these pieces would come from different peers over individual TCP packets. */ export class MetadataReassembler { private pieces: Map = new Map(); private totalSize: number; private pieceSize: number = 16384; // 16KiB constructor(totalSize: number) { this.totalSize = totalSize; } public addPiece(index: number, data: Uint8Array): boolean { this.pieces.set(index, data); return this.isComplete(); } public isComplete(): boolean { const totalPieces = Math.ceil(this.totalSize / this.pieceSize); return this.pieces.size === totalPieces; } public getFullMetadata(): Uint8Array | null { if (!this.isComplete()) return null; const fullData = new Uint8Array(this.totalSize); const sortedIndices = Array.from(this.pieces.keys()).sort((a, b) => a - b); let offset = 0; for (const index of sortedIndices) { const piece = this.pieces.get(index)!; fullData.set(piece, offset); offset += piece.length; } return fullData; } public getProgress(): number { const totalPieces = Math.ceil(this.totalSize / this.pieceSize); return (this.pieces.size / totalPieces) * 100; } } /** * Tracks which pieces we have and provides bitfield generation. */ export class Bitfield { private bits: Uint8Array; public totalPieces: number; constructor(totalPieces: number) { this.totalPieces = totalPieces; this.bits = new Uint8Array(Math.ceil(totalPieces / 8)); } public set(index: number) { if (index >= this.totalPieces) return; const byteIndex = Math.floor(index / 8); const bitIndex = 7 - (index % 8); this.bits[byteIndex] |= (1 << bitIndex); } public has(index: number): boolean { if (index >= this.totalPieces) return false; const byteIndex = Math.floor(index / 8); const bitIndex = 7 - (index % 8); return (this.bits[byteIndex] & (1 << bitIndex)) !== 0; } public toBuffer(): Uint8Array { return this.bits; } public fromBuffer(buffer: Uint8Array) { this.bits = new Uint8Array(buffer); } }