Files
TorrentClient/packages/shared/src/protocol.ts
Peter Stockings 8bcf8a43fe Initial commit
2026-01-01 16:11:06 +11:00

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);
}
}