You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-07 13:22:56 +03:00
test RESP decoder + fix some bugs
This commit is contained in:
@@ -1,226 +1,286 @@
|
|||||||
// import { strict as assert } from 'node:assert';
|
import { strict as assert } from 'node:assert';
|
||||||
// import { SinonSpy, spy } from 'sinon';
|
import { SinonSpy, spy } from 'sinon';
|
||||||
// import { Decoder, RESP_TYPES } from './decoder';
|
import { Decoder, RESP_TYPES } from './decoder';
|
||||||
// import { ErrorReply } from '../errors';
|
import { BlobError, ErrorReply, SimpleError } from '../errors';
|
||||||
// import { TypeMapping } from './types';
|
import { TypeMapping } from './types';
|
||||||
|
|
||||||
// function createDecoderAndSpies(typeMapping: TypeMapping = {}) {
|
interface Test {
|
||||||
// const typeMappingSpy = spy(() => typeMapping),
|
toWrite: Buffer;
|
||||||
// onReplySpy = spy(),
|
typeMapping?: TypeMapping;
|
||||||
// onErrorReplySpy = spy(),
|
replies?: Array<unknown>;
|
||||||
// onPushSpy = spy();
|
errorReplies?: Array<unknown>;
|
||||||
|
pushReplies?: Array<unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
// return {
|
function test(name: string, config: Test) {
|
||||||
// decoder: new Decoder({
|
describe(name, () => {
|
||||||
// getTypeMapping: typeMappingSpy,
|
it('single chunk', () => {
|
||||||
// onReply: onReplySpy,
|
const setup = setupTest(config);
|
||||||
// onErrorReply: onErrorReplySpy,
|
setup.decoder.write(config.toWrite);
|
||||||
// onPush: onPushSpy
|
assertCalls(config, setup);
|
||||||
// }),
|
});
|
||||||
// typeMappingSpy,
|
|
||||||
// onReplySpy,
|
|
||||||
// onErrorReplySpy,
|
|
||||||
// onPushSpy
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function writeChunks(stream: Decoder, buffer: Buffer) {
|
it('byte by byte', () => {
|
||||||
// let i = 0;
|
const setup = setupTest(config);
|
||||||
// while (i < buffer.length) {
|
for (let i = 0; i < config.toWrite.length; i++) {
|
||||||
// stream.write(buffer.subarray(i, ++i));
|
setup.decoder.write(config.toWrite.subarray(i, i + 1));
|
||||||
// }
|
}
|
||||||
// }
|
assertCalls(config, setup);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// type Replies = Array<Array<unknown>>;
|
function setupTest(config: Test) {
|
||||||
|
const onReplySpy = spy(),
|
||||||
|
onErrorReplySpy = spy(),
|
||||||
|
onPushSpy = spy();
|
||||||
|
|
||||||
// function generateTests(toWrite: Buffer, tests: Array<Test & { name: string }>): void {
|
return {
|
||||||
// for (const test of tests) {
|
decoder: new Decoder({
|
||||||
// describe(test.name, () => {
|
getTypeMapping: () => config.typeMapping ?? {},
|
||||||
// generateTests(toWrite, test);
|
onReply: onReplySpy,
|
||||||
// });
|
onErrorReply: onErrorReplySpy,
|
||||||
// }
|
onPush: onPushSpy
|
||||||
// }
|
}),
|
||||||
|
onReplySpy,
|
||||||
|
onErrorReplySpy,
|
||||||
|
onPushSpy
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// interface Test {
|
function assertCalls(config: Test, spies: ReturnType<typeof setupTest>) {
|
||||||
// typeMapping?: TypeMapping;
|
assertSpyCalls(spies.onReplySpy, config.replies);
|
||||||
// replies?: Replies;
|
assertSpyCalls(spies.onErrorReplySpy, config.errorReplies);
|
||||||
// errorReplies?: Replies;
|
assertSpyCalls(spies.onPushSpy, config.pushReplies);
|
||||||
// pushReplies?: Replies;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// function genetareTypeTests(toWrite: Buffer, { typeMapping, replies, errorReplies, pushReplies }: Test) {
|
function assertSpyCalls(spy: SinonSpy, replies?: Array<unknown>) {
|
||||||
// const total = (replies?.length ?? 0) + (errorReplies?.length ?? 0) + (pushReplies?.length ?? 0);
|
if (!replies) {
|
||||||
// it('single chunk', () => {
|
assert.equal(spy.callCount, 0);
|
||||||
// const { decoder, typeMappingSpy, onReplySpy, onErrorReplySpy, onPushSpy } = createDecoderAndSpies(typeMapping);
|
return;
|
||||||
// decoder.write(toWrite);
|
}
|
||||||
// assert.equal(typeMappingSpy.callCount, total);
|
|
||||||
// testReplies(onReplySpy, replies);
|
|
||||||
// testReplies(onErrorReplySpy, errorReplies);
|
|
||||||
// testReplies(onPushSpy, pushReplies);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('multiple chunks', () => {
|
assert.equal(spy.callCount, replies.length);
|
||||||
// const { decoder, typeMappingSpy, onReplySpy, onErrorReplySpy, onPushSpy } = createDecoderAndSpies(typeMapping);
|
for (const [i, reply] of replies.entries()) {
|
||||||
// writeChunks(decoder, toWrite);
|
assert.deepEqual(
|
||||||
// assert.equal(typeMappingSpy.callCount, total);
|
spy.getCall(i).args,
|
||||||
// testReplies(onReplySpy, replies);
|
[reply]
|
||||||
// testReplies(onErrorReplySpy, errorReplies);
|
);
|
||||||
// testReplies(onPushSpy, pushReplies);
|
}
|
||||||
// });
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// function testReplies(spy: SinonSpy, replies?: Replies): void {
|
describe.only('RESP Decoder', () => {
|
||||||
// if (!replies) {
|
test('Null', {
|
||||||
// assert.equal(spy.callCount, 0);
|
toWrite: Buffer.from('_\r\n'),
|
||||||
// return;
|
replies: [null]
|
||||||
// }
|
});
|
||||||
|
|
||||||
// assert.equal(spy.callCount, replies.length);
|
describe('Boolean', () => {
|
||||||
// for (const [i, reply] of replies.entries()) {
|
test('true', {
|
||||||
// assert.deepEqual(
|
toWrite: Buffer.from('#t\r\n'),
|
||||||
// spy.getCall(i).args,
|
replies: [true]
|
||||||
// reply
|
});
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// describe('RESP2Parser', () => {
|
test('false', {
|
||||||
// describe('Null', () => {
|
toWrite: Buffer.from('#f\r\n'),
|
||||||
// genetareTypeTests(Buffer.from('_\r\n'), {
|
replies: [false]
|
||||||
// replies: [[null]]
|
});
|
||||||
// });
|
});
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('Boolean', () => {
|
describe('Number', () => {
|
||||||
// genetareTypeTests(Buffer.from('#t\r\n'), {
|
test('0', {
|
||||||
// replies: [[null]]
|
toWrite: Buffer.from(':0\r\n'),
|
||||||
// });
|
replies: [0]
|
||||||
// });
|
});
|
||||||
|
|
||||||
// describe('Number', () => {
|
test('1', {
|
||||||
// generateTests(Buffer.from(':-1\r\n'))
|
toWrite: Buffer.from(':+1\r\n'),
|
||||||
// describe('as number', () => {
|
replies: [1]
|
||||||
// describe('-1', () => {
|
});
|
||||||
// generateTests({
|
|
||||||
// toWrite: ,
|
|
||||||
// replies: [[-1]]
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('0', () => {
|
test('+1', {
|
||||||
// generateTests({
|
toWrite: Buffer.from(':+1\r\n'),
|
||||||
// toWrite: Buffer.from(':0\r\n'),
|
replies: [1]
|
||||||
// replies: [[0]]
|
});
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('+1', () => {
|
test('-1', {
|
||||||
// generateTests({
|
toWrite: Buffer.from(':-1\r\n'),
|
||||||
// toWrite: Buffer.from(':+1\r\n'),
|
replies: [-1]
|
||||||
// replies: [[1]]
|
});
|
||||||
// });
|
});
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('Simple String', () => {
|
describe('BigNumber', () => {
|
||||||
// describe('as strings', () => {
|
test('0', {
|
||||||
// generateTests({
|
toWrite: Buffer.from('(0\r\n'),
|
||||||
// toWrite: Buffer.from('+OK\r\n'),
|
replies: [0n]
|
||||||
// replies: [['OK']]
|
});
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('as buffers', () => {
|
test('1', {
|
||||||
// generateTests({
|
toWrite: Buffer.from('(1\r\n'),
|
||||||
// toWrite: Buffer.from('+OK\r\n'),
|
replies: [1n]
|
||||||
// typeMapping: {
|
});
|
||||||
// [RESP_TYPES.SIMPLE_STRING]: Buffer
|
|
||||||
// },
|
|
||||||
// replies: [[Buffer.from('OK')]]
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('Error', () => {
|
test('+1', {
|
||||||
// generateTests({
|
toWrite: Buffer.from('(+1\r\n'),
|
||||||
// toWrite: Buffer.from('-ERR\r\n'),
|
replies: [1n]
|
||||||
// errorReplies: [[new ErrorReply('ERR')]]
|
});
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
test('-1', {
|
||||||
|
toWrite: Buffer.from('(-1\r\n'),
|
||||||
|
replies: [-1n]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Double', () => {
|
||||||
|
test('0', {
|
||||||
|
toWrite: Buffer.from(',0\r\n'),
|
||||||
|
replies: [0]
|
||||||
|
});
|
||||||
|
|
||||||
// describe('Bulk String', () => {
|
test('1', {
|
||||||
// describe('null', () => {
|
toWrite: Buffer.from(',1\r\n'),
|
||||||
// generateTests({
|
replies: [1]
|
||||||
// toWrite: Buffer.from('$-1\r\n'),
|
});
|
||||||
// returnStringsAsBuffers: false,
|
|
||||||
// replies: [[null]]
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('as strings', () => {
|
test('+1', {
|
||||||
// generateTests({
|
toWrite: Buffer.from(',+1\r\n'),
|
||||||
// toWrite: Buffer.from('$2\r\naa\r\n'),
|
replies: [1]
|
||||||
// returnStringsAsBuffers: false,
|
});
|
||||||
// replies: [['aa']]
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('as buffers', () => {
|
test('-1', {
|
||||||
// generateTests({
|
toWrite: Buffer.from(',-1\r\n'),
|
||||||
// toWrite: Buffer.from('$2\r\naa\r\n'),
|
replies: [-1]
|
||||||
// returnStringsAsBuffers: true,
|
});
|
||||||
// replies: [[Buffer.from('aa')]]
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('Array', () => {
|
test('1.1', {
|
||||||
// describe('null', () => {
|
toWrite: Buffer.from(',1.1\r\n'),
|
||||||
// generateTests({
|
replies: [1.1]
|
||||||
// toWrite: Buffer.from('*-1\r\n'),
|
});
|
||||||
// returnStringsAsBuffers: false,
|
|
||||||
// replies: [[null]]
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const arrayBuffer = Buffer.from(
|
test('nan', {
|
||||||
// '*5\r\n' +
|
toWrite: Buffer.from(',nan\r\n'),
|
||||||
// '+OK\r\n' +
|
replies: [NaN]
|
||||||
// '-ERR\r\n' +
|
});
|
||||||
// ':0\r\n' +
|
|
||||||
// '$1\r\na\r\n' +
|
|
||||||
// '*0\r\n'
|
|
||||||
// );
|
|
||||||
|
|
||||||
// describe('as strings', () => {
|
test('inf', {
|
||||||
// generateTests({
|
toWrite: Buffer.from(',inf\r\n'),
|
||||||
// toWrite: arrayBuffer,
|
replies: [Infinity]
|
||||||
// returnStringsAsBuffers: false,
|
});
|
||||||
// replies: [[[
|
|
||||||
// 'OK',
|
|
||||||
// new ErrorReply('ERR'),
|
|
||||||
// 0,
|
|
||||||
// 'a',
|
|
||||||
// []
|
|
||||||
// ]]]
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('as buffers', () => {
|
test('+inf', {
|
||||||
// generateTests({
|
toWrite: Buffer.from(',+inf\r\n'),
|
||||||
// toWrite: arrayBuffer,
|
replies: [Infinity]
|
||||||
// returnStringsAsBuffers: true,
|
});
|
||||||
// replies: [[[
|
|
||||||
// Buffer.from('OK'),
|
test('-inf', {
|
||||||
// new ErrorReply('ERR'),
|
toWrite: Buffer.from(',-inf\r\n'),
|
||||||
// 0,
|
replies: [-Infinity]
|
||||||
// Buffer.from('a'),
|
});
|
||||||
// []
|
|
||||||
// ]]]
|
test('1e1', {
|
||||||
// });
|
toWrite: Buffer.from(',1e1\r\n'),
|
||||||
// });
|
replies: [1e1]
|
||||||
// });
|
});
|
||||||
// });
|
|
||||||
|
test('-1.1E1', {
|
||||||
|
toWrite: Buffer.from(',-1.1E1\r\n'),
|
||||||
|
replies: [-1.1E1]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('SimpleString', {
|
||||||
|
toWrite: Buffer.from('+OK\r\n'),
|
||||||
|
replies: ['OK']
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('BlobString', () => {
|
||||||
|
test("''", {
|
||||||
|
toWrite: Buffer.from('$0\r\n\r\n'),
|
||||||
|
replies: ['']
|
||||||
|
});
|
||||||
|
|
||||||
|
test("'1234567890'", {
|
||||||
|
toWrite: Buffer.from('$10\r\n1234567890\r\n'),
|
||||||
|
replies: ['1234567890']
|
||||||
|
});
|
||||||
|
|
||||||
|
test('null (RESP2 backwards compatibility)', {
|
||||||
|
toWrite: Buffer.from('$-1\r\n'),
|
||||||
|
replies: [null]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('VerbatimString', () => {
|
||||||
|
test("''", {
|
||||||
|
toWrite: Buffer.from('=4\r\ntxt:\r\n'),
|
||||||
|
replies: ['']
|
||||||
|
});
|
||||||
|
|
||||||
|
test("'123456'", {
|
||||||
|
toWrite: Buffer.from('=10\r\ntxt:123456\r\n'),
|
||||||
|
replies: ['123456']
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('SimpleError', {
|
||||||
|
toWrite: Buffer.from('-ERROR\r\n'),
|
||||||
|
errorReplies: [new SimpleError('ERROR')]
|
||||||
|
});
|
||||||
|
|
||||||
|
test('BlobError', {
|
||||||
|
toWrite: Buffer.from('!5\r\nERROR\r\n'),
|
||||||
|
errorReplies: [new BlobError('ERROR')]
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Array', () => {
|
||||||
|
test('[]', {
|
||||||
|
toWrite: Buffer.from('*0\r\n'),
|
||||||
|
replies: [[]]
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[0..9]', {
|
||||||
|
toWrite: Buffer.from(`*10\r\n:0\r\n:1\r\n:2\r\n:3\r\n:4\r\n:5\r\n:6\r\n:7\r\n:8\r\n:9\r\n`),
|
||||||
|
replies: [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]
|
||||||
|
});
|
||||||
|
|
||||||
|
test('null (RESP2 backwards compatibility)', {
|
||||||
|
toWrite: Buffer.from('*-1\r\n'),
|
||||||
|
replies: [null]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Set', () => {
|
||||||
|
test('empty', {
|
||||||
|
toWrite: Buffer.from('~0\r\n'),
|
||||||
|
replies: [[]]
|
||||||
|
});
|
||||||
|
|
||||||
|
test('of 0..9', {
|
||||||
|
toWrite: Buffer.from(`*10\r\n:0\r\n:1\r\n:2\r\n:3\r\n:4\r\n:5\r\n:6\r\n:7\r\n:8\r\n:9\r\n`),
|
||||||
|
replies: [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Map', () => {
|
||||||
|
test('{}', {
|
||||||
|
toWrite: Buffer.from('%0\r\n'),
|
||||||
|
replies: [Object.create(null)]
|
||||||
|
});
|
||||||
|
|
||||||
|
test("{ '0'..'9': <key> }", {
|
||||||
|
toWrite: Buffer.from(`%10\r\n+0\r\n+0\r\n+1\r\n+1\r\n+2\r\n+2\r\n+3\r\n+3\r\n+4\r\n+4\r\n+5\r\n+5\r\n+6\r\n+6\r\n+7\r\n+7\r\n+8\r\n+8\r\n+9\r\n+9\r\n`),
|
||||||
|
replies: [Object.create(null, {
|
||||||
|
0: { value: '0', enumerable: true },
|
||||||
|
1: { value: '1', enumerable: true },
|
||||||
|
2: { value: '2', enumerable: true },
|
||||||
|
3: { value: '3', enumerable: true },
|
||||||
|
4: { value: '4', enumerable: true },
|
||||||
|
5: { value: '5', enumerable: true },
|
||||||
|
6: { value: '6', enumerable: true },
|
||||||
|
7: { value: '7', enumerable: true },
|
||||||
|
8: { value: '8', enumerable: true },
|
||||||
|
9: { value: '9', enumerable: true }
|
||||||
|
})]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@@ -257,7 +257,7 @@ export class Decoder {
|
|||||||
private _maybeDecodeNumberValue(isNegative, chunk) {
|
private _maybeDecodeNumberValue(isNegative, chunk) {
|
||||||
const cb = this._decodeUnsingedNumber.bind(this, 0);
|
const cb = this._decodeUnsingedNumber.bind(this, 0);
|
||||||
return ++this._cursor === chunk.length ?
|
return ++this._cursor === chunk.length ?
|
||||||
this._decodeNumberValue.bind(isNegative, cb) :
|
this._decodeNumberValue.bind(this, isNegative, cb) :
|
||||||
this._decodeNumberValue(isNegative, cb, chunk);
|
this._decodeNumberValue(isNegative, cb, chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,8 +306,8 @@ export class Decoder {
|
|||||||
|
|
||||||
private _maybeDecodeBigNumberValue(isNegative, chunk) {
|
private _maybeDecodeBigNumberValue(isNegative, chunk) {
|
||||||
const cb = this._decodeUnsingedBigNumber.bind(this, 0n);
|
const cb = this._decodeUnsingedBigNumber.bind(this, 0n);
|
||||||
return this._cursor === chunk.length ?
|
return ++this._cursor === chunk.length ?
|
||||||
this._decodeBigNumberValue.bind(isNegative, cb) :
|
this._decodeBigNumberValue.bind(this, isNegative, cb) :
|
||||||
this._decodeBigNumberValue(isNegative, cb, chunk);
|
this._decodeBigNumberValue(isNegative, cb, chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,7 +357,7 @@ export class Decoder {
|
|||||||
private _maybeDecodeDoubleInteger(isNegative, chunk) {
|
private _maybeDecodeDoubleInteger(isNegative, chunk) {
|
||||||
return ++this._cursor === chunk.length ?
|
return ++this._cursor === chunk.length ?
|
||||||
this._decodeDoubleInteger.bind(this, isNegative, 0) :
|
this._decodeDoubleInteger.bind(this, isNegative, 0) :
|
||||||
this._decodeDoubleInteger(true, 0, chunk);
|
this._decodeDoubleInteger(isNegative, 0, chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _decodeDoubleInteger(isNegative, integer, chunk) {
|
private _decodeDoubleInteger(isNegative, integer, chunk) {
|
||||||
@@ -376,14 +376,17 @@ export class Decoder {
|
|||||||
switch (byte) {
|
switch (byte) {
|
||||||
case ASCII['.']:
|
case ASCII['.']:
|
||||||
this._cursor = cursor + 1; // skip .
|
this._cursor = cursor + 1; // skip .
|
||||||
return cursor < chunk.length ?
|
return this._cursor < chunk.length ?
|
||||||
this._decodeDoubleDecimal(isNegative, 0, integer, chunk) :
|
this._decodeDoubleDecimal(isNegative, 0, integer, chunk) :
|
||||||
this._decodeDoubleDecimal.bind(this, isNegative, 0, integer);
|
this._decodeDoubleDecimal.bind(this, isNegative, 0, integer);
|
||||||
|
|
||||||
case ASCII.E:
|
case ASCII.E:
|
||||||
case ASCII.e:
|
case ASCII.e:
|
||||||
this._cursor = cursor + 1; // skip e
|
this._cursor = cursor + 1; // skip E/e
|
||||||
return this._decodeDoubleExponent(isNegative ? -integer : integer, chunk);
|
const i = isNegative ? -integer : integer;
|
||||||
|
return this._cursor < chunk.length ?
|
||||||
|
this._decodeDoubleExponent(i, chunk) :
|
||||||
|
this._decodeDoubleExponent.bind(this, i);
|
||||||
|
|
||||||
case ASCII['\r']:
|
case ASCII['\r']:
|
||||||
this._cursor = cursor + 2; // skip \r\n
|
this._cursor = cursor + 2; // skip \r\n
|
||||||
@@ -414,11 +417,11 @@ export class Decoder {
|
|||||||
switch (byte) {
|
switch (byte) {
|
||||||
case ASCII.E:
|
case ASCII.E:
|
||||||
case ASCII.e:
|
case ASCII.e:
|
||||||
this._cursor = cursor + 1; // skip e
|
this._cursor = cursor + 1; // skip E/e
|
||||||
const d = isNegative ? -double : double;
|
const d = isNegative ? -double : double;
|
||||||
return this._cursor === chunk.length ?
|
return this._cursor === chunk.length ?
|
||||||
this._decodeDoubleExponent.bind(this, d, false, 0) :
|
this._decodeDoubleExponent.bind(this, d) :
|
||||||
this._decodeDoubleExponent(d, false, 0, chunk);
|
this._decodeDoubleExponent(d, chunk);
|
||||||
|
|
||||||
case ASCII['\r']:
|
case ASCII['\r']:
|
||||||
this._cursor = cursor + 2; // skip \r\n
|
this._cursor = cursor + 2; // skip \r\n
|
||||||
|
Reference in New Issue
Block a user