Skip to content

Commit

Permalink
refactor: use BufferReader in metadataParse
Browse files Browse the repository at this point in the history
  • Loading branch information
mShan0 committed Aug 11, 2022
1 parent c210729 commit 310b4d4
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 89 deletions.
111 changes: 33 additions & 78 deletions src/metadata-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 0 additions & 2 deletions src/token/colmetadata-token-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}
Expand Down
13 changes: 6 additions & 7 deletions src/token/returnvalue-token-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/value-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
});

Expand Down

0 comments on commit 310b4d4

Please sign in to comment.