Initial commit
This commit is contained in:
97
packages/shared/src/protocol.ts
Normal file
97
packages/shared/src/protocol.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user