From d7326334368ff7eb14b8f65bda529ce626d9188f Mon Sep 17 00:00:00 2001 From: mShan0 <96149598+mShan0@users.noreply.github.com> Date: Thu, 4 Aug 2022 12:57:50 -0700 Subject: [PATCH 1/8] refactor: refactor `LoginAckToken` parser --- src/token/loginack-token-parser.ts | 115 +++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 29 deletions(-) diff --git a/src/token/loginack-token-parser.ts b/src/token/loginack-token-parser.ts index b28c47330..2408d5eee 100644 --- a/src/token/loginack-token-parser.ts +++ b/src/token/loginack-token-parser.ts @@ -4,42 +4,99 @@ import { LoginAckToken } from './token'; import { versionsByValue as versions } from '../tds-versions'; +class NotEnoughDataError extends Error { } + const interfaceTypes: { [key: number]: string } = { 0: 'SQL_DFLT', 1: 'SQL_TSQL' }; +let offset: number; + +function checkDataLength(buffer: Buffer, numBytes: number): void { + if (buffer.length < offset + numBytes) { + throw new NotEnoughDataError(); + } +} + +function readUInt16LE(parser: Parser): number { + const numBytes = 2; + checkDataLength(parser.buffer, numBytes); + const data = parser.buffer.readUInt16LE(offset); + offset += numBytes; + return data; +} + +function readUInt8(parser: Parser): number { + const numBytes = 1; + checkDataLength(parser.buffer, numBytes); + const data = parser.buffer.readUInt8(offset); + offset += numBytes; + return data; +} + +function readUInt32BE(parser: Parser): number { + const numBytes = 4; + checkDataLength(parser.buffer, numBytes); + const data = parser.buffer.readUInt32BE(offset); + offset += numBytes; + return data; +} + +function readBVarChar(parser: Parser): string { + const numBytes = readUInt8(parser) * 2; + const data = readFromBuffer(parser, numBytes).toString('ucs2'); + return data; +} + +function readFromBuffer(parser: Parser, numBytes: number): Buffer { + checkDataLength(parser.buffer, numBytes); + const result = parser.buffer.slice(offset, offset + numBytes); + offset += numBytes; + return result; +} + +function parseToken(parser: Parser): LoginAckToken { + offset = parser.position; + readUInt16LE(parser); + const interfaceNumber = readUInt8(parser); + const interfaceType = interfaceTypes[interfaceNumber]; + const tdsVersionNumber = readUInt32BE(parser); + const tdsVersion = versions[tdsVersionNumber]; + const progName = readBVarChar(parser); + const major = readUInt8(parser); + const minor = readUInt8(parser); + const buildNumHi = readUInt8(parser); + const buildNumLow = readUInt8(parser); + + parser.position = offset; + + return new LoginAckToken({ + interface: interfaceType, + tdsVersion: tdsVersion, + progName: progName, + progVersion: { + major: major, + minor: minor, + buildNumHi: buildNumHi, + buildNumLow: buildNumLow + } + }); +} + function loginAckParser(parser: Parser, _options: ParserOptions, callback: (token: LoginAckToken) => void) { - // length - parser.readUInt16LE(() => { - parser.readUInt8((interfaceNumber) => { - const interfaceType = interfaceTypes[interfaceNumber]; - parser.readUInt32BE((tdsVersionNumber) => { - const tdsVersion = versions[tdsVersionNumber]; - parser.readBVarChar((progName) => { - parser.readUInt8((major) => { - parser.readUInt8((minor) => { - parser.readUInt8((buildNumHi) => { - parser.readUInt8((buildNumLow) => { - callback(new LoginAckToken({ - interface: interfaceType, - tdsVersion: tdsVersion, - progName: progName, - progVersion: { - major: major, - minor: minor, - buildNumHi: buildNumHi, - buildNumLow: buildNumLow - } - })); - }); - }); - }); - }); - }); + let data!: LoginAckToken; + try { + data = parseToken(parser); + } catch (err) { + if (err instanceof NotEnoughDataError) { + return parser.suspend(() => { + loginAckParser(parser, _options, callback); }); - }); - }); + } + } + + callback(data); } export default loginAckParser; From 6b6e91d3f0027e1ae37582a9aaf6289c639e3fc2 Mon Sep 17 00:00:00 2001 From: mShan0 <96149598+mShan0@users.noreply.github.com> Date: Thu, 4 Aug 2022 13:14:53 -0700 Subject: [PATCH 2/8] refactor: refactor `OrderToken` --- src/token/order-token-parser.ts | 58 +++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/src/token/order-token-parser.ts b/src/token/order-token-parser.ts index 3f8a5792a..b1c9d8932 100644 --- a/src/token/order-token-parser.ts +++ b/src/token/order-token-parser.ts @@ -3,30 +3,52 @@ import Parser, { ParserOptions } from './stream-parser'; import { OrderToken } from './token'; -function orderParser(parser: Parser, _options: ParserOptions, callback: (token: OrderToken) => void) { - parser.readUInt16LE((length) => { - const columnCount = length / 2; - const orderColumns: number[] = []; +class NotEnoughDataError extends Error { } + +let offset: number; + +function checkDataLength(buffer: Buffer, numBytes: number): void { + if (buffer.length < offset + numBytes) { + throw new NotEnoughDataError(); + } +} + +function readUInt16LE(parser: Parser): number { + const numBytes = 2; + checkDataLength(parser.buffer, numBytes); + const data = parser.buffer.readUInt16LE(offset); + offset += numBytes; + return data; +} + +function parseToken(parser: Parser): OrderToken { + offset = parser.position; - let i = 0; - function next(done: () => void) { - if (i === columnCount) { - return done(); - } + const length = readUInt16LE(parser); + const columnCount = length / 2; + const orderColumns: number[] = []; - parser.readUInt16LE((column) => { - orderColumns.push(column); + for (let i = 0; i < columnCount; i++) { + const column = readUInt16LE(parser); + orderColumns.push(column); + } - i++; + parser.position = offset; + return new OrderToken(orderColumns); +} - next(done); +function orderParser(parser: Parser, _options: ParserOptions, callback: (token: OrderToken) => void) { + let data!: OrderToken; + try { + data = parseToken(parser); + } catch (err) { + if (err instanceof NotEnoughDataError) { + return parser.suspend(() => { + orderParser(parser, _options, callback); }); } - - next(() => { - callback(new OrderToken(orderColumns)); - }); - }); + } + callback(data); } export default orderParser; From ff4686b06fdccfc9fe082d5cd872840c970002df Mon Sep 17 00:00:00 2001 From: mShan0 <96149598+mShan0@users.noreply.github.com> Date: Thu, 4 Aug 2022 09:54:16 -0700 Subject: [PATCH 3/8] refactor: refactor `returnstatusparser` --- src/token/returnstatus-token-parser.ts | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/token/returnstatus-token-parser.ts b/src/token/returnstatus-token-parser.ts index a925d3dbf..04a267558 100644 --- a/src/token/returnstatus-token-parser.ts +++ b/src/token/returnstatus-token-parser.ts @@ -3,10 +3,30 @@ import Parser, { ParserOptions } from './stream-parser'; import { ReturnStatusToken } from './token'; +class NotEnoughDataError extends Error { } + +function parseToken(parser: Parser): number { + const buffer = parser.buffer; + if (buffer.length < parser.position + 4) { + throw new NotEnoughDataError(); + } + const data = parser.buffer.readInt32LE(parser.position); + parser.position += 4; + return data; +} + function returnStatusParser(parser: Parser, _options: ParserOptions, callback: (token: ReturnStatusToken) => void) { - parser.readInt32LE((value) => { - callback(new ReturnStatusToken(value)); - }); + let data!: number; + try { + data = parseToken(parser); + } catch (err) { + if (err instanceof NotEnoughDataError) { + return parser.suspend(() => { + returnStatusParser(parser, _options, callback); + }); + } + } + callback(new ReturnStatusToken(data)); } export default returnStatusParser; From 1d6bb823b553d1c172fededd441474fa6c825735 Mon Sep 17 00:00:00 2001 From: mShan0 <96149598+mShan0@users.noreply.github.com> Date: Fri, 5 Aug 2022 12:42:11 -0700 Subject: [PATCH 4/8] refactor: refactor `infoerror` parsers --- src/token/infoerror-token-parser.ts | 129 +++++++++++++++++++++------- 1 file changed, 97 insertions(+), 32 deletions(-) diff --git a/src/token/infoerror-token-parser.ts b/src/token/infoerror-token-parser.ts index b55acec09..90fa3c299 100644 --- a/src/token/infoerror-token-parser.ts +++ b/src/token/infoerror-token-parser.ts @@ -2,6 +2,59 @@ import Parser, { ParserOptions } from './stream-parser'; import { InfoMessageToken, ErrorMessageToken } from './token'; +class NotEnoughDataError extends Error { } +let offset: number; + +function checkDataLength(buffer: Buffer, numBytes: number): void { + if (buffer.length < offset + numBytes) { + throw new NotEnoughDataError(); + } +} + +function readUInt16LE(parser: Parser): number { + const numBytes = 2; + checkDataLength(parser.buffer, numBytes); + const data = parser.buffer.readUInt16LE(offset); + offset += numBytes; + return data; +} + +function readUInt8(parser: Parser): number { + const numBytes = 1; + checkDataLength(parser.buffer, numBytes); + const data = parser.buffer.readUInt8(offset); + offset += numBytes; + return data; +} + +function readUInt32LE(parser: Parser): number { + const numBytes = 4; + checkDataLength(parser.buffer, numBytes); + const data = parser.buffer.readUInt32LE(offset); + offset += numBytes; + return data; +} + +function readBVarChar(parser: Parser): string { + const numBytes = readUInt8(parser) * 2; + const data = readFromBuffer(parser, numBytes).toString('ucs2'); + return data; +} + + +function readUsVarChar(parser: Parser): string { + const numBytes = readUInt16LE(parser) * 2; + const data = readFromBuffer(parser, numBytes).toString('ucs2'); + return data; +} + +function readFromBuffer(parser: Parser, numBytes: number): Buffer { + checkDataLength(parser.buffer, numBytes); + const result = parser.buffer.slice(offset, offset + numBytes); + offset += numBytes; + return result; +} + interface TokenData { number: number; state: number; @@ -12,43 +65,55 @@ interface TokenData { lineNumber: number; } -function parseToken(parser: Parser, options: ParserOptions, callback: (data: TokenData) => void) { +function parseToken(parser: Parser, options: ParserOptions): TokenData { // length - parser.readUInt16LE(() => { - parser.readUInt32LE((number) => { - parser.readUInt8((state) => { - parser.readUInt8((clazz) => { - parser.readUsVarChar((message) => { - parser.readBVarChar((serverName) => { - parser.readBVarChar((procName) => { - (options.tdsVersion < '7_2' ? parser.readUInt16LE : parser.readUInt32LE).call(parser, (lineNumber: number) => { - callback({ - 'number': number, - 'state': state, - 'class': clazz, - 'message': message, - 'serverName': serverName, - 'procName': procName, - 'lineNumber': lineNumber - }); - }); - }); - }); - }); - }); - }); - }); - }); + offset = parser.position; + readUInt16LE(parser); + const number = readUInt32LE(parser); + const state = readUInt8(parser); + const clazz = readUInt8(parser); + const message = readUsVarChar(parser); + const serverName = readBVarChar(parser); + const procName = readBVarChar(parser); + const lineNumber = options.tdsVersion < '7_2' ? readUInt16LE(parser) : readUInt32LE(parser); + parser.position = offset; + return { + 'number': number, + 'state': state, + 'class': clazz, + 'message': message, + 'serverName': serverName, + 'procName': procName, + 'lineNumber': lineNumber + } as TokenData; } export function infoParser(parser: Parser, options: ParserOptions, callback: (token: InfoMessageToken) => void) { - parseToken(parser, options, (data) => { - callback(new InfoMessageToken(data)); - }); + let data!: TokenData; + try { + data = parseToken(parser, options); + } catch (err) { + if (err instanceof NotEnoughDataError) { + return parser.suspend(() => { + infoParser(parser, options, callback); + }); + } + } + + callback(new InfoMessageToken(data)); } export function errorParser(parser: Parser, options: ParserOptions, callback: (token: ErrorMessageToken) => void) { - parseToken(parser, options, (data) => { - callback(new ErrorMessageToken(data)); - }); + let data!: TokenData; + try { + data = parseToken(parser, options); + } catch (err) { + if (err instanceof NotEnoughDataError) { + return parser.suspend(() => { + errorParser(parser, options, callback); + }); + } + } + + callback(new ErrorMessageToken(data)); } From b1f7798b57590a02e6178fedd37f9f9e00a07a3d Mon Sep 17 00:00:00 2001 From: mShan0 <96149598+mShan0@users.noreply.github.com> Date: Wed, 10 Aug 2022 09:37:44 -0700 Subject: [PATCH 5/8] refactor: move buffer reads into separate class --- src/token/buffer-reader.ts | 84 ++++++++++++++++++++++++++ src/token/infoerror-token-parser.ts | 72 +++------------------- src/token/loginack-token-parser.ts | 66 +++----------------- src/token/order-token-parser.ts | 24 ++------ src/token/returnstatus-token-parser.ts | 11 ++-- 5 files changed, 112 insertions(+), 145 deletions(-) create mode 100644 src/token/buffer-reader.ts diff --git a/src/token/buffer-reader.ts b/src/token/buffer-reader.ts new file mode 100644 index 000000000..c7c629202 --- /dev/null +++ b/src/token/buffer-reader.ts @@ -0,0 +1,84 @@ +import Parser from './stream-parser'; + +class NotEnoughDataError extends Error { } + + +export default class BufferReader { + offset: number; + parser: Parser; + + constructor(parser: Parser) { + this.offset = parser.position; + this.parser = parser; + } + + checkDataLength(buffer: Buffer, numBytes: number): void { + if (buffer.length < this.parser.position + numBytes) { + this.parser.position = this.offset; + throw new NotEnoughDataError(); + } + } + + readUInt8(): number { + const numBytes = 1; + this.checkDataLength(this.parser.buffer, numBytes); + const data = this.parser.buffer.readUInt8(this.parser.position); + this.parser.position += numBytes; + return data; + } + + readUInt16LE(): number { + const numBytes = 2; + this.checkDataLength(this.parser.buffer, numBytes); + const data = this.parser.buffer.readUInt16LE(this.parser.position); + this.parser.position += numBytes; + return data; + } + + readUInt32LE(): number { + const numBytes = 4; + this.checkDataLength(this.parser.buffer, numBytes); + const data = this.parser.buffer.readUInt32LE(this.parser.position); + this.parser.position += numBytes; + return data; + } + + readUInt32BE(): number { + const numBytes = 4; + this.checkDataLength(this.parser.buffer, numBytes); + const data = this.parser.buffer.readUInt32BE(this.parser.position); + this.parser.position += numBytes; + return data; + } + + readInt32LE(): number { + const numBytes = 4; + this.checkDataLength(this.parser.buffer, numBytes); + const data = this.parser.buffer.readInt32LE(this.parser.position); + this.parser.position += numBytes; + return data; + } + + readBVarChar(): string { + const numBytes = this.readUInt8() * 2; + const data = this.readFromBuffer(numBytes).toString('ucs2'); + return data; + } + + + readUsVarChar(): string { + const numBytes = this.readUInt16LE() * 2; + const data = this.readFromBuffer(numBytes).toString('ucs2'); + return data; + } + + readFromBuffer(numBytes: number): Buffer { + this.checkDataLength(this.parser.buffer, numBytes); + const result = this.parser.buffer.slice(this.parser.position, this.parser.position + numBytes); + this.parser.position += numBytes; + return result; + } + +} + +module.exports = BufferReader; diff --git a/src/token/infoerror-token-parser.ts b/src/token/infoerror-token-parser.ts index 90fa3c299..d498d606e 100644 --- a/src/token/infoerror-token-parser.ts +++ b/src/token/infoerror-token-parser.ts @@ -1,59 +1,8 @@ import Parser, { ParserOptions } from './stream-parser'; - +import BufferReader from './buffer-reader'; import { InfoMessageToken, ErrorMessageToken } from './token'; class NotEnoughDataError extends Error { } -let offset: number; - -function checkDataLength(buffer: Buffer, numBytes: number): void { - if (buffer.length < offset + numBytes) { - throw new NotEnoughDataError(); - } -} - -function readUInt16LE(parser: Parser): number { - const numBytes = 2; - checkDataLength(parser.buffer, numBytes); - const data = parser.buffer.readUInt16LE(offset); - offset += numBytes; - return data; -} - -function readUInt8(parser: Parser): number { - const numBytes = 1; - checkDataLength(parser.buffer, numBytes); - const data = parser.buffer.readUInt8(offset); - offset += numBytes; - return data; -} - -function readUInt32LE(parser: Parser): number { - const numBytes = 4; - checkDataLength(parser.buffer, numBytes); - const data = parser.buffer.readUInt32LE(offset); - offset += numBytes; - return data; -} - -function readBVarChar(parser: Parser): string { - const numBytes = readUInt8(parser) * 2; - const data = readFromBuffer(parser, numBytes).toString('ucs2'); - return data; -} - - -function readUsVarChar(parser: Parser): string { - const numBytes = readUInt16LE(parser) * 2; - const data = readFromBuffer(parser, numBytes).toString('ucs2'); - return data; -} - -function readFromBuffer(parser: Parser, numBytes: number): Buffer { - checkDataLength(parser.buffer, numBytes); - const result = parser.buffer.slice(offset, offset + numBytes); - offset += numBytes; - return result; -} interface TokenData { number: number; @@ -67,16 +16,15 @@ interface TokenData { function parseToken(parser: Parser, options: ParserOptions): TokenData { // length - offset = parser.position; - readUInt16LE(parser); - const number = readUInt32LE(parser); - const state = readUInt8(parser); - const clazz = readUInt8(parser); - const message = readUsVarChar(parser); - const serverName = readBVarChar(parser); - const procName = readBVarChar(parser); - const lineNumber = options.tdsVersion < '7_2' ? readUInt16LE(parser) : readUInt32LE(parser); - parser.position = offset; + const br = new BufferReader(parser); + br.readUInt16LE(); + const number = br.readUInt32LE(); + const state = br.readUInt8(); + const clazz = br.readUInt8(); + const message = br.readUsVarChar(); + const serverName = br.readBVarChar(); + const procName = br.readBVarChar(); + const lineNumber = options.tdsVersion < '7_2' ? br.readUInt16LE() : br.readUInt32LE(); return { 'number': number, 'state': state, diff --git a/src/token/loginack-token-parser.ts b/src/token/loginack-token-parser.ts index 2408d5eee..55f96cb35 100644 --- a/src/token/loginack-token-parser.ts +++ b/src/token/loginack-token-parser.ts @@ -3,6 +3,7 @@ import Parser, { ParserOptions } from './stream-parser'; import { LoginAckToken } from './token'; import { versionsByValue as versions } from '../tds-versions'; +import BufferReader from './buffer-reader'; class NotEnoughDataError extends Error { } @@ -11,65 +12,18 @@ const interfaceTypes: { [key: number]: string } = { 1: 'SQL_TSQL' }; -let offset: number; - -function checkDataLength(buffer: Buffer, numBytes: number): void { - if (buffer.length < offset + numBytes) { - throw new NotEnoughDataError(); - } -} - -function readUInt16LE(parser: Parser): number { - const numBytes = 2; - checkDataLength(parser.buffer, numBytes); - const data = parser.buffer.readUInt16LE(offset); - offset += numBytes; - return data; -} - -function readUInt8(parser: Parser): number { - const numBytes = 1; - checkDataLength(parser.buffer, numBytes); - const data = parser.buffer.readUInt8(offset); - offset += numBytes; - return data; -} - -function readUInt32BE(parser: Parser): number { - const numBytes = 4; - checkDataLength(parser.buffer, numBytes); - const data = parser.buffer.readUInt32BE(offset); - offset += numBytes; - return data; -} - -function readBVarChar(parser: Parser): string { - const numBytes = readUInt8(parser) * 2; - const data = readFromBuffer(parser, numBytes).toString('ucs2'); - return data; -} - -function readFromBuffer(parser: Parser, numBytes: number): Buffer { - checkDataLength(parser.buffer, numBytes); - const result = parser.buffer.slice(offset, offset + numBytes); - offset += numBytes; - return result; -} - function parseToken(parser: Parser): LoginAckToken { - offset = parser.position; - readUInt16LE(parser); - const interfaceNumber = readUInt8(parser); + const br = new BufferReader(parser); + br.readUInt16LE(); + const interfaceNumber = br.readUInt8(); const interfaceType = interfaceTypes[interfaceNumber]; - const tdsVersionNumber = readUInt32BE(parser); + const tdsVersionNumber = br.readUInt32BE(); const tdsVersion = versions[tdsVersionNumber]; - const progName = readBVarChar(parser); - const major = readUInt8(parser); - const minor = readUInt8(parser); - const buildNumHi = readUInt8(parser); - const buildNumLow = readUInt8(parser); - - parser.position = offset; + const progName = br.readBVarChar(); + const major = br.readUInt8(); + const minor = br.readUInt8(); + const buildNumHi = br.readUInt8(); + const buildNumLow = br.readUInt8(); return new LoginAckToken({ interface: interfaceType, diff --git a/src/token/order-token-parser.ts b/src/token/order-token-parser.ts index b1c9d8932..0bc143b75 100644 --- a/src/token/order-token-parser.ts +++ b/src/token/order-token-parser.ts @@ -1,39 +1,23 @@ // s2.2.7.14 +import BufferReader from './buffer-reader'; import Parser, { ParserOptions } from './stream-parser'; import { OrderToken } from './token'; class NotEnoughDataError extends Error { } -let offset: number; - -function checkDataLength(buffer: Buffer, numBytes: number): void { - if (buffer.length < offset + numBytes) { - throw new NotEnoughDataError(); - } -} - -function readUInt16LE(parser: Parser): number { - const numBytes = 2; - checkDataLength(parser.buffer, numBytes); - const data = parser.buffer.readUInt16LE(offset); - offset += numBytes; - return data; -} - function parseToken(parser: Parser): OrderToken { - offset = parser.position; + const br = new BufferReader(parser); - const length = readUInt16LE(parser); + const length = br.readUInt16LE(); const columnCount = length / 2; const orderColumns: number[] = []; for (let i = 0; i < columnCount; i++) { - const column = readUInt16LE(parser); + const column = br.readUInt16LE(); orderColumns.push(column); } - parser.position = offset; return new OrderToken(orderColumns); } diff --git a/src/token/returnstatus-token-parser.ts b/src/token/returnstatus-token-parser.ts index 04a267558..819637219 100644 --- a/src/token/returnstatus-token-parser.ts +++ b/src/token/returnstatus-token-parser.ts @@ -1,4 +1,5 @@ // s2.2.7.16 +import BufferReader from './buffer-reader'; import Parser, { ParserOptions } from './stream-parser'; import { ReturnStatusToken } from './token'; @@ -6,13 +7,9 @@ import { ReturnStatusToken } from './token'; class NotEnoughDataError extends Error { } function parseToken(parser: Parser): number { - const buffer = parser.buffer; - if (buffer.length < parser.position + 4) { - throw new NotEnoughDataError(); - } - const data = parser.buffer.readInt32LE(parser.position); - parser.position += 4; - return data; + const br = new BufferReader(parser); + const value = br.readInt32LE(); + return value; } function returnStatusParser(parser: Parser, _options: ParserOptions, callback: (token: ReturnStatusToken) => void) { From c21072954e939932a28b028e3068e9ba91c25f0f Mon Sep 17 00:00:00 2001 From: mShan0 <96149598+mShan0@users.noreply.github.com> Date: Wed, 13 Jul 2022 11:43:30 -0700 Subject: [PATCH 6/8] refactor: refactor `metadataParse` --- src/metadata-parser.ts | 445 +++++++++++--------------- src/token/colmetadata-token-parser.ts | 44 ++- src/token/returnvalue-token-parser.ts | 42 ++- src/value-parser.ts | 10 +- 4 files changed, 251 insertions(+), 290 deletions(-) diff --git a/src/metadata-parser.ts b/src/metadata-parser.ts index 5108fd1f3..5e5e02d29 100644 --- a/src/metadata-parser.ts +++ b/src/metadata-parser.ts @@ -53,268 +53,205 @@ export type Metadata = { cryptoMetadata?: CryptoMetadata; } & BaseMetadata; +class UnknownTypeError extends Error { } +class NotEnoughDataError extends Error { } -function readCollation(parser: Parser, callback: (collation: Collation) => void) { +function checkDataLength(buffer: Buffer, offset: number, numBytes: number): void { + if (buffer.length < offset + numBytes) { + throw new NotEnoughDataError(); + } +} + +function readFromBuffer(parser: Parser, length: number): Buffer { + checkDataLength(parser.buffer, parser.position, length); + const result = parser.buffer.slice(parser.position, parser.position + length); + parser.position += length; + return result; +} + +function readUInt8(parser: Parser): number { + checkDataLength(parser.buffer, parser.position, 1); + const data = parser.buffer.readUInt8(parser.position); + parser.position += 1; + return data; +} + +function readUInt16LE(parser: Parser): number { + checkDataLength(parser.buffer, parser.position, 2); + const data = parser.buffer.readUInt16LE(parser.position); + parser.position += 2; + return data; +} + +function readUInt32LE(parser: Parser): number { + checkDataLength(parser.buffer, parser.position, 4); + const data = parser.buffer.readUInt32LE(parser.position); + parser.position += 4; + return data; +} + +function readBVarChar(parser: Parser): string { + const length = readUInt8(parser) * 2; + const data = readFromBuffer(parser, length).toString('ucs2'); + return data; +} + +function readUsVarChar(parser: Parser): string { + const length = readUInt16LE(parser) * 2; + const data = readFromBuffer(parser, length).toString('ucs2'); + return data; +} + +function readCollation(parser: Parser): Collation { // s2.2.5.1.2 - parser.readBuffer(5, (collationData) => { - callback(Collation.fromBuffer(collationData)); - }); + const collationData = readFromBuffer(parser, 5); + return Collation.fromBuffer(collationData); } -function readSchema(parser: Parser, callback: (schema: XmlSchema | undefined) => void) { - // s2.2.5.5.3 - parser.readUInt8((schemaPresent) => { - if (schemaPresent === 0x01) { - parser.readBVarChar((dbname) => { - parser.readBVarChar((owningSchema) => { - parser.readUsVarChar((xmlSchemaCollection) => { - callback({ - dbname: dbname, - owningSchema: owningSchema, - xmlSchemaCollection: xmlSchemaCollection - }); - }); - }); - }); - } else { - callback(undefined); - } - }); +function readSchema(parser: Parser): XmlSchema | undefined { + const schemaPresent = readUInt8(parser); + if (schemaPresent === 0x01) { + const dbname = readBVarChar(parser); + const owningSchema = readBVarChar(parser); + const xmlSchemaCollection = readUsVarChar(parser); + return { + dbname: dbname, + owningSchema: owningSchema, + xmlSchemaCollection: xmlSchemaCollection + }; + } else { + return undefined; + } } -function readUDTInfo(parser: Parser, callback: (udtInfo: UdtInfo | undefined) => void) { - parser.readUInt16LE((maxByteSize) => { - parser.readBVarChar((dbname) => { - parser.readBVarChar((owningSchema) => { - parser.readBVarChar((typeName) => { - parser.readUsVarChar((assemblyName) => { - callback({ - maxByteSize: maxByteSize, - dbname: dbname, - owningSchema: owningSchema, - typeName: typeName, - assemblyName: assemblyName - }); - }); - }); - }); - }); - }); +function readUDTInfo(parser: Parser) { + const maxByteSize = readUInt16LE(parser); + const dbname = readBVarChar(parser); + const owningSchema = readBVarChar(parser); + const typeName = readBVarChar(parser); + const assemblyName = readUsVarChar(parser); + return { + maxByteSize: maxByteSize, + dbname: dbname, + owningSchema: owningSchema, + typeName: typeName, + assemblyName: assemblyName + }; } -function metadataParse(parser: Parser, options: ParserOptions, callback: (metadata: Metadata) => void) { - (options.tdsVersion < '7_2' ? parser.readUInt16LE : parser.readUInt32LE).call(parser, (userType) => { - parser.readUInt16LE((flags) => { - parser.readUInt8((typeNumber) => { - const type: DataType = TYPE[typeNumber]; - - if (!type) { - throw new Error(sprintf('Unrecognised data type 0x%02X', typeNumber)); - } - - switch (type.name) { - case 'Null': - case 'TinyInt': - case 'SmallInt': - case 'Int': - case 'BigInt': - case 'Real': - case 'Float': - case 'SmallMoney': - case 'Money': - case 'Bit': - case 'SmallDateTime': - case 'DateTime': - case 'Date': - return callback({ - userType: userType, - flags: flags, - type: type, - collation: undefined, - precision: undefined, - scale: undefined, - dataLength: undefined, - schema: undefined, - udtInfo: undefined - }); - - case 'IntN': - case 'FloatN': - case 'MoneyN': - case 'BitN': - case 'UniqueIdentifier': - case 'DateTimeN': - return parser.readUInt8((dataLength) => { - callback({ - userType: userType, - flags: flags, - type: type, - collation: undefined, - precision: undefined, - scale: undefined, - dataLength: dataLength, - schema: undefined, - udtInfo: undefined - }); - }); - - case 'Variant': - return parser.readUInt32LE((dataLength) => { - callback({ - userType: userType, - flags: flags, - type: type, - collation: undefined, - precision: undefined, - scale: undefined, - dataLength: dataLength, - schema: undefined, - udtInfo: undefined - }); - }); - - case 'VarChar': - case 'Char': - case 'NVarChar': - case 'NChar': - return parser.readUInt16LE((dataLength) => { - readCollation(parser, (collation) => { - callback({ - userType: userType, - flags: flags, - type: type, - collation: collation, - precision: undefined, - scale: undefined, - dataLength: dataLength, - schema: undefined, - udtInfo: undefined - }); - }); - }); - - case 'Text': - case 'NText': - return parser.readUInt32LE((dataLength) => { - readCollation(parser, (collation) => { - callback({ - userType: userType, - flags: flags, - type: type, - collation: collation, - precision: undefined, - scale: undefined, - dataLength: dataLength, - schema: undefined, - udtInfo: undefined - }); - }); - }); - - case 'VarBinary': - case 'Binary': - return parser.readUInt16LE((dataLength) => { - callback({ - userType: userType, - flags: flags, - type: type, - collation: undefined, - precision: undefined, - scale: undefined, - dataLength: dataLength, - schema: undefined, - udtInfo: undefined - }); - }); - - case 'Image': - return parser.readUInt32LE((dataLength) => { - callback({ - userType: userType, - flags: flags, - type: type, - collation: undefined, - precision: undefined, - scale: undefined, - dataLength: dataLength, - schema: undefined, - udtInfo: undefined - }); - }); - - case 'Xml': - return readSchema(parser, (schema) => { - callback({ - userType: userType, - flags: flags, - type: type, - collation: undefined, - precision: undefined, - scale: undefined, - dataLength: undefined, - schema: schema, - udtInfo: undefined - }); - }); - - case 'Time': - case 'DateTime2': - case 'DateTimeOffset': - return parser.readUInt8((scale) => { - callback({ - userType: userType, - flags: flags, - type: type, - collation: undefined, - precision: undefined, - scale: scale, - dataLength: undefined, - schema: undefined, - udtInfo: undefined - }); - }); - - case 'NumericN': - case 'DecimalN': - return parser.readUInt8((dataLength) => { - parser.readUInt8((precision) => { - parser.readUInt8((scale) => { - callback({ - userType: userType, - flags: flags, - type: type, - collation: undefined, - precision: precision, - scale: scale, - dataLength: dataLength, - schema: undefined, - udtInfo: undefined - }); - }); - }); - }); - - case 'UDT': - return readUDTInfo(parser, (udtInfo) => { - callback({ - userType: userType, - flags: flags, - type: type, - collation: undefined, - precision: undefined, - scale: undefined, - dataLength: undefined, - schema: undefined, - udtInfo: udtInfo - }); - }); - - default: - throw new Error(sprintf('Unrecognised type %s', type.name)); - } - }); - }); - }); +function metadataParse(parser: Parser, options: ParserOptions): Metadata { + let userType: number; + + if (options.tdsVersion < '7_2') { + userType = readUInt16LE(parser); + } else { + userType = readUInt32LE(parser); + } + + const flags = readUInt16LE(parser); + + const typeNumber = readUInt8(parser); + const type: DataType = TYPE[typeNumber]; + + let collation: Collation | undefined; + let precision: number | undefined; + let scale: number | undefined; + let dataLength: number | undefined; + let schema: XmlSchema | undefined; + let udtInfo: UdtInfo | undefined; + + if (!type) { + throw new UnknownTypeError(sprintf('Unrecognised data type 0x%02X', typeNumber)); + } + + switch (type.name) { + case 'Null': + case 'TinyInt': + case 'SmallInt': + case 'Int': + case 'BigInt': + case 'Real': + case 'Float': + case 'SmallMoney': + case 'Money': + case 'Bit': + case 'SmallDateTime': + case 'DateTime': + case 'Date': + break; + + case 'IntN': + case 'FloatN': + case 'MoneyN': + case 'BitN': + case 'UniqueIdentifier': + case 'DateTimeN': + dataLength = readUInt8(parser); + break; + + case 'Variant': + dataLength = readUInt32LE(parser); + break; + + case 'VarChar': + case 'Char': + case 'NVarChar': + case 'NChar': + dataLength = readUInt16LE(parser); + collation = readCollation(parser); + break; + + case 'Text': + case 'NText': + dataLength = readUInt32LE(parser); + collation = readCollation(parser); + break; + + case 'VarBinary': + case 'Binary': + dataLength = readUInt16LE(parser); + break; + + case 'Image': + dataLength = readUInt32LE(parser); + break; + + case 'Xml': + schema = readSchema(parser); + break; + + case 'Time': + case 'DateTime2': + case 'DateTimeOffset': + scale = readUInt8(parser); + break; + + case 'NumericN': + case 'DecimalN': + dataLength = readUInt8(parser); + precision = readUInt8(parser); + scale = readUInt8(parser); + break; + + case 'UDT': + udtInfo = readUDTInfo(parser); + break; + + default: + throw new UnknownTypeError(sprintf('Unrecognised type %s', type.name)); + } + + return { + userType: userType, + flags: flags, + type: type, + collation: collation, + precision: precision, + scale: scale, + dataLength: dataLength, + schema: schema, + udtInfo: udtInfo + }; } export default metadataParse; diff --git a/src/token/colmetadata-token-parser.ts b/src/token/colmetadata-token-parser.ts index cab333c5a..23be005d1 100644 --- a/src/token/colmetadata-token-parser.ts +++ b/src/token/colmetadata-token-parser.ts @@ -12,6 +12,8 @@ export interface ColumnMetadata extends Metadata { tableName?: string | string[] | undefined; } +class NotEnoughDataError extends Error { } + function readTableName(parser: Parser, options: ParserOptions, metadata: Metadata, callback: (tableName?: string | string[]) => void) { if (metadata.type.hasTableName) { if (options.tdsVersion >= '7_2') { @@ -60,22 +62,32 @@ function readColumnName(parser: Parser, options: ParserOptions, index: number, m } function readColumn(parser: Parser, options: ParserOptions, index: number, callback: (column: ColumnMetadata) => void) { - metadataParse(parser, options, (metadata) => { - readTableName(parser, options, metadata, (tableName) => { - readColumnName(parser, options, index, metadata, (colName) => { - callback({ - userType: metadata.userType, - flags: metadata.flags, - type: metadata.type, - collation: metadata.collation, - precision: metadata.precision, - scale: metadata.scale, - udtInfo: metadata.udtInfo, - dataLength: metadata.dataLength, - schema: metadata.schema, - colName: colName, - tableName: tableName - }); + let metadata!: Metadata; + const offset = parser.position; + try { + metadata = metadataParse(parser, options); + } catch (err) { + if (err instanceof NotEnoughDataError) { + return parser.suspend(() => { + parser.position = offset; + readColumn(parser, options, index, callback); + }); + } + } + readTableName(parser, options, metadata, (tableName) => { + readColumnName(parser, options, index, metadata, (colName) => { + callback({ + userType: metadata.userType, + flags: metadata.flags, + type: metadata.type, + collation: metadata.collation, + precision: metadata.precision, + scale: metadata.scale, + udtInfo: metadata.udtInfo, + dataLength: metadata.dataLength, + schema: metadata.schema, + colName: colName, + tableName: tableName }); }); }); diff --git a/src/token/returnvalue-token-parser.ts b/src/token/returnvalue-token-parser.ts index 42699497a..baadba6f4 100644 --- a/src/token/returnvalue-token-parser.ts +++ b/src/token/returnvalue-token-parser.ts @@ -4,32 +4,46 @@ import Parser, { ParserOptions } from './stream-parser'; import { ReturnValueToken } from './token'; -import metadataParse from '../metadata-parser'; +import metadataParse, { Metadata } from '../metadata-parser'; import valueParse from '../value-parser'; +class NotEnoughDataError extends Error { } + function returnParser(parser: Parser, options: ParserOptions, callback: (token: ReturnValueToken) => void) { parser.readUInt16LE((paramOrdinal) => { parser.readBVarChar((paramName) => { if (paramName.charAt(0) === '@') { paramName = paramName.slice(1); } - + parser.position += 1; // status - parser.readUInt8(() => { - metadataParse(parser, options, (metadata) => { - valueParse(parser, metadata, options, (value) => { - callback(new ReturnValueToken({ - paramOrdinal: paramOrdinal, - paramName: paramName, - metadata: metadata, - value: value - })); - }); - }); - }); + readValue(parser, options, paramOrdinal, paramName, parser.position, callback); + }); }); } +function readValue(parser: Parser, options: ParserOptions, paramOrdinal: number, paramName: string, originalPosition: number, callback: (token: ReturnValueToken) => void) { + let metadata!: Metadata; + parser.position = originalPosition; + try { + metadata = metadataParse(parser, options); + } catch (err) { + if (err instanceof NotEnoughDataError) { + return parser.suspend(() => { + readValue(parser, options, paramOrdinal, paramName, originalPosition, callback); + }); + } + } + valueParse(parser, metadata, options, (value) => { + callback(new ReturnValueToken({ + paramOrdinal: paramOrdinal, + paramName: paramName, + metadata: metadata, + value: value + })); + }); +} + export default returnParser; module.exports = returnParser; diff --git a/src/value-parser.ts b/src/value-parser.ts index 2c1f303c6..21669153c 100644 --- a/src/value-parser.ts +++ b/src/value-parser.ts @@ -458,17 +458,15 @@ function readVariant(parser: Parser, options: ParserOptions, dataLength: number, case 'VarChar': case 'Char': return parser.readUInt16LE((_maxLength) => { - readCollation(parser, (collation) => { - readChars(parser, dataLength, collation.codepage!, callback); - }); + const collation = readCollation(parser); + readChars(parser, dataLength, collation.codepage!, callback); }); case 'NVarChar': case 'NChar': return parser.readUInt16LE((_maxLength) => { - readCollation(parser, (_collation) => { - readNChars(parser, dataLength, callback); - }); + readCollation(parser); + readNChars(parser, dataLength, callback); }); default: From 310b4d48f14b72035b35215ab9eaa33a4da3ccaa Mon Sep 17 00:00:00 2001 From: mShan0 <96149598+mShan0@users.noreply.github.com> Date: Wed, 10 Aug 2022 11:35:03 -0700 Subject: [PATCH 7/8] refactor: use `BufferReader` in `metadataParse` --- src/metadata-parser.ts | 111 ++++++++------------------ src/token/colmetadata-token-parser.ts | 2 - src/token/returnvalue-token-parser.ts | 13 ++- src/value-parser.ts | 7 +- 4 files changed, 44 insertions(+), 89 deletions(-) diff --git a/src/metadata-parser.ts b/src/metadata-parser.ts index 5e5e02d29..818793248 100644 --- a/src/metadata-parser.ts +++ b/src/metadata-parser.ts @@ -4,6 +4,7 @@ import { TYPE, DataType } from './data-type'; import { CryptoMetadata } from './always-encrypted/types'; import { sprintf } from 'sprintf-js'; +import BufferReader from './token/buffer-reader'; interface XmlSchema { dbname: string; @@ -54,66 +55,19 @@ export type Metadata = { } & BaseMetadata; class UnknownTypeError extends Error { } -class NotEnoughDataError extends Error { } -function checkDataLength(buffer: Buffer, offset: number, numBytes: number): void { - if (buffer.length < offset + numBytes) { - throw new NotEnoughDataError(); - } -} - -function readFromBuffer(parser: Parser, length: number): Buffer { - checkDataLength(parser.buffer, parser.position, length); - const result = parser.buffer.slice(parser.position, parser.position + length); - parser.position += length; - return result; -} - -function readUInt8(parser: Parser): number { - checkDataLength(parser.buffer, parser.position, 1); - const data = parser.buffer.readUInt8(parser.position); - parser.position += 1; - return data; -} - -function readUInt16LE(parser: Parser): number { - checkDataLength(parser.buffer, parser.position, 2); - const data = parser.buffer.readUInt16LE(parser.position); - parser.position += 2; - return data; -} - -function readUInt32LE(parser: Parser): number { - checkDataLength(parser.buffer, parser.position, 4); - const data = parser.buffer.readUInt32LE(parser.position); - parser.position += 4; - return data; -} - -function readBVarChar(parser: Parser): string { - const length = readUInt8(parser) * 2; - const data = readFromBuffer(parser, length).toString('ucs2'); - return data; -} - -function readUsVarChar(parser: Parser): string { - const length = readUInt16LE(parser) * 2; - const data = readFromBuffer(parser, length).toString('ucs2'); - return data; -} - -function readCollation(parser: Parser): Collation { +function readCollation(br: BufferReader): Collation { // s2.2.5.1.2 - const collationData = readFromBuffer(parser, 5); + const collationData = br.readFromBuffer(5); return Collation.fromBuffer(collationData); } -function readSchema(parser: Parser): XmlSchema | undefined { - const schemaPresent = readUInt8(parser); +function readSchema(br: BufferReader): XmlSchema | undefined { + const schemaPresent = br.readUInt8(); if (schemaPresent === 0x01) { - const dbname = readBVarChar(parser); - const owningSchema = readBVarChar(parser); - const xmlSchemaCollection = readUsVarChar(parser); + const dbname = br.readBVarChar(); + const owningSchema = br.readBVarChar(); + const xmlSchemaCollection = br.readUsVarChar(); return { dbname: dbname, owningSchema: owningSchema, @@ -124,12 +78,12 @@ function readSchema(parser: Parser): XmlSchema | undefined { } } -function readUDTInfo(parser: Parser) { - const maxByteSize = readUInt16LE(parser); - const dbname = readBVarChar(parser); - const owningSchema = readBVarChar(parser); - const typeName = readBVarChar(parser); - const assemblyName = readUsVarChar(parser); +function readUDTInfo(br: BufferReader): UdtInfo { + const maxByteSize = br.readUInt16LE(); + const dbname = br.readBVarChar(); + const owningSchema = br.readBVarChar(); + const typeName = br.readBVarChar(); + const assemblyName = br.readUsVarChar(); return { maxByteSize: maxByteSize, dbname: dbname, @@ -141,16 +95,17 @@ function readUDTInfo(parser: Parser) { function metadataParse(parser: Parser, options: ParserOptions): Metadata { let userType: number; + const br = new BufferReader(parser); if (options.tdsVersion < '7_2') { - userType = readUInt16LE(parser); + userType = br.readUInt16LE(); } else { - userType = readUInt32LE(parser); + userType = br.readUInt32LE(); } - const flags = readUInt16LE(parser); + const flags = br.readUInt16LE(); - const typeNumber = readUInt8(parser); + const typeNumber = br.readUInt8(); const type: DataType = TYPE[typeNumber]; let collation: Collation | undefined; @@ -186,55 +141,55 @@ function metadataParse(parser: Parser, options: ParserOptions): Metadata { case 'BitN': case 'UniqueIdentifier': case 'DateTimeN': - dataLength = readUInt8(parser); + dataLength = br.readUInt8(); break; case 'Variant': - dataLength = readUInt32LE(parser); + dataLength = br.readUInt32LE(); break; case 'VarChar': case 'Char': case 'NVarChar': case 'NChar': - dataLength = readUInt16LE(parser); - collation = readCollation(parser); + dataLength = br.readUInt16LE(); + collation = readCollation(br); break; case 'Text': case 'NText': - dataLength = readUInt32LE(parser); - collation = readCollation(parser); + dataLength = br.readUInt32LE(); + collation = readCollation(br); break; case 'VarBinary': case 'Binary': - dataLength = readUInt16LE(parser); + dataLength = br.readUInt16LE(); break; case 'Image': - dataLength = readUInt32LE(parser); + dataLength = br.readUInt32LE(); break; case 'Xml': - schema = readSchema(parser); + schema = readSchema(br); break; case 'Time': case 'DateTime2': case 'DateTimeOffset': - scale = readUInt8(parser); + scale = br.readUInt8(); break; case 'NumericN': case 'DecimalN': - dataLength = readUInt8(parser); - precision = readUInt8(parser); - scale = readUInt8(parser); + dataLength = br.readUInt8(); + precision = br.readUInt8(); + scale = br.readUInt8(); break; case 'UDT': - udtInfo = readUDTInfo(parser); + udtInfo = readUDTInfo(br); break; default: diff --git a/src/token/colmetadata-token-parser.ts b/src/token/colmetadata-token-parser.ts index 23be005d1..403ae9449 100644 --- a/src/token/colmetadata-token-parser.ts +++ b/src/token/colmetadata-token-parser.ts @@ -63,13 +63,11 @@ function readColumnName(parser: Parser, options: ParserOptions, index: number, m function readColumn(parser: Parser, options: ParserOptions, index: number, callback: (column: ColumnMetadata) => void) { let metadata!: Metadata; - const offset = parser.position; try { metadata = metadataParse(parser, options); } catch (err) { if (err instanceof NotEnoughDataError) { return parser.suspend(() => { - parser.position = offset; readColumn(parser, options, index, callback); }); } diff --git a/src/token/returnvalue-token-parser.ts b/src/token/returnvalue-token-parser.ts index baadba6f4..d5f8efbe9 100644 --- a/src/token/returnvalue-token-parser.ts +++ b/src/token/returnvalue-token-parser.ts @@ -15,23 +15,22 @@ function returnParser(parser: Parser, options: ParserOptions, callback: (token: if (paramName.charAt(0) === '@') { paramName = paramName.slice(1); } - parser.position += 1; - // status - readValue(parser, options, paramOrdinal, paramName, parser.position, callback); - + parser.readUInt8(() => { + // status + readValue(parser, options, paramOrdinal, paramName, callback); + }); }); }); } -function readValue(parser: Parser, options: ParserOptions, paramOrdinal: number, paramName: string, originalPosition: number, callback: (token: ReturnValueToken) => void) { +function readValue(parser: Parser, options: ParserOptions, paramOrdinal: number, paramName: string, callback: (token: ReturnValueToken) => void) { let metadata!: Metadata; - parser.position = originalPosition; try { metadata = metadataParse(parser, options); } catch (err) { if (err instanceof NotEnoughDataError) { return parser.suspend(() => { - readValue(parser, options, paramOrdinal, paramName, originalPosition, callback); + readValue(parser, options, paramOrdinal, paramName, callback); }); } } diff --git a/src/value-parser.ts b/src/value-parser.ts index 21669153c..fa9ce4740 100644 --- a/src/value-parser.ts +++ b/src/value-parser.ts @@ -5,6 +5,7 @@ import { TYPE } from './data-type'; import iconv from 'iconv-lite'; import { sprintf } from 'sprintf-js'; import { bufferToLowerCaseGuid, bufferToUpperCaseGuid } from './guid-parser'; +import BufferReader from './token/buffer-reader'; const NULL = (1 << 16) - 1; const MAX = (1 << 16) - 1; @@ -458,14 +459,16 @@ function readVariant(parser: Parser, options: ParserOptions, dataLength: number, case 'VarChar': case 'Char': return parser.readUInt16LE((_maxLength) => { - const collation = readCollation(parser); + const br = new BufferReader(parser); + const collation = readCollation(br); readChars(parser, dataLength, collation.codepage!, callback); }); case 'NVarChar': case 'NChar': return parser.readUInt16LE((_maxLength) => { - readCollation(parser); + const br = new BufferReader(parser); + readCollation(br); readNChars(parser, dataLength, callback); }); From a0eebf9f94a1b72a1ff5e615815166f2df1410a2 Mon Sep 17 00:00:00 2001 From: mShan0 <96149598+mShan0@users.noreply.github.com> Date: Fri, 12 Aug 2022 09:43:18 -0700 Subject: [PATCH 8/8] refactor: make some fields private in `BufferReader` --- src/token/buffer-reader.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/token/buffer-reader.ts b/src/token/buffer-reader.ts index c7c629202..5d892cfd1 100644 --- a/src/token/buffer-reader.ts +++ b/src/token/buffer-reader.ts @@ -4,7 +4,7 @@ class NotEnoughDataError extends Error { } export default class BufferReader { - offset: number; + private offset: number; parser: Parser; constructor(parser: Parser) { @@ -12,7 +12,7 @@ export default class BufferReader { this.parser = parser; } - checkDataLength(buffer: Buffer, numBytes: number): void { + private checkDataLength(buffer: Buffer, numBytes: number): void { if (buffer.length < this.parser.position + numBytes) { this.parser.position = this.offset; throw new NotEnoughDataError();