@hazae41/kcp
Version:
Zero-copy KCP protocol for the web
95 lines (91 loc) • 3.6 kB
JavaScript
;
var binary = require('@hazae41/binary');
var cursor = require('@hazae41/cursor');
var index = require('../console/index.cjs');
var segment = require('./segment.cjs');
class ExpectedKcpSegmentError extends Error {
#class = ExpectedKcpSegmentError;
name = this.#class.name;
constructor() {
super(`Expected a KCP segment`);
}
}
class UnknownKcpCommandError extends Error {
#class = UnknownKcpCommandError;
name = this.#class.name;
constructor() {
super(`Unknown KCP command`);
}
}
class SecretKcpReader {
parent;
#buffer = new Map();
constructor(parent) {
this.parent = parent;
}
async onWrite(chunk) {
const cursor$1 = new cursor.Cursor(chunk.bytes);
while (cursor$1.remaining)
await this.#onSegment(binary.Readable.readOrRollbackAndThrow(segment.KcpSegment, cursor$1));
return;
}
async #onSegment(segment$1) {
if (segment$1.conversation !== this.parent.conversation)
return;
if (segment$1.command === segment.KcpSegment.commands.push)
return await this.#onPushSegment(segment$1);
if (segment$1.command === segment.KcpSegment.commands.ack)
return await this.#onAckSegment(segment$1);
if (segment$1.command === segment.KcpSegment.commands.wask)
return await this.#onWaskSegment(segment$1);
throw new UnknownKcpCommandError();
}
async #onPushSegment(segment$1) {
const conversation = this.parent.conversation;
const command = segment.KcpSegment.commands.ack;
const timestamp = segment$1.timestamp;
const serial = segment$1.serial;
const unackSerial = this.parent.recvCounter;
const fragment = new binary.Empty();
const ack = segment.KcpSegment.empty({ conversation, command, timestamp, serial, unackSerial, fragment });
this.parent.output.enqueue(ack);
if (segment$1.serial < this.parent.recvCounter) {
index.Console.debug(`Received previous KCP segment`);
return;
}
if (segment$1.serial > this.parent.recvCounter) {
index.Console.debug(`Received next KCP segment`);
this.#buffer.set(segment$1.serial, segment$1);
return;
}
this.parent.input.enqueue(segment$1.fragment);
this.parent.recvCounter++;
let next;
while (next = this.#buffer.get(this.parent.recvCounter)) {
index.Console.debug(`Unblocked next KCP segment`);
this.parent.input.enqueue(next.fragment);
this.#buffer.delete(this.parent.recvCounter);
this.parent.recvCounter++;
}
}
async #onAckSegment(segment) {
const future = this.parent.resolveOnAckBySerial.get(segment.serial);
if (future == null)
return;
this.parent.resolveOnAckBySerial.delete(segment.serial);
future.resolve();
}
async #onWaskSegment(segment$1) {
const conversation = this.parent.conversation;
const command = segment.KcpSegment.commands.wins;
const serial = 0;
const unackSerial = this.parent.recvCounter;
const fragment = new binary.Empty();
const wins = segment.KcpSegment.empty({ conversation, command, serial, unackSerial, fragment });
this.parent.output.enqueue(wins);
}
}
exports.ExpectedKcpSegmentError = ExpectedKcpSegmentError;
exports.SecretKcpReader = SecretKcpReader;
exports.UnknownKcpCommandError = UnknownKcpCommandError;
//# sourceMappingURL=reader.cjs.map