98 lines
2.8 KiB
TypeScript
98 lines
2.8 KiB
TypeScript
/**
|
|
* 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<number, Uint8Array> = 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);
|
|
}
|
|
}
|