"use strict";
|
|
const Transform = require('readable-stream').Transform;
|
|
const TYPE = require('./token').TYPE;
|
|
const tokenParsers = {};
|
tokenParsers[TYPE.COLMETADATA] = require('./colmetadata-token-parser');
|
tokenParsers[TYPE.DONE] = require('./done-token-parser').doneParser;
|
tokenParsers[TYPE.DONEINPROC] = require('./done-token-parser').doneInProcParser;
|
tokenParsers[TYPE.DONEPROC] = require('./done-token-parser').doneProcParser;
|
tokenParsers[TYPE.ENVCHANGE] = require('./env-change-token-parser');
|
tokenParsers[TYPE.ERROR] = require('./infoerror-token-parser').errorParser;
|
tokenParsers[TYPE.FEDAUTHINFO] = require('./fedauth-info-parser');
|
tokenParsers[TYPE.FEATUREEXTACK] = require('./feature-ext-ack-parser');
|
tokenParsers[TYPE.INFO] = require('./infoerror-token-parser').infoParser;
|
tokenParsers[TYPE.LOGINACK] = require('./loginack-token-parser');
|
tokenParsers[TYPE.ORDER] = require('./order-token-parser');
|
tokenParsers[TYPE.RETURNSTATUS] = require('./returnstatus-token-parser');
|
tokenParsers[TYPE.RETURNVALUE] = require('./returnvalue-token-parser');
|
tokenParsers[TYPE.ROW] = require('./row-token-parser');
|
tokenParsers[TYPE.NBCROW] = require('./nbcrow-token-parser');
|
tokenParsers[TYPE.SSPI] = require('./sspi-token-parser');
|
module.exports = class Parser extends Transform {
|
constructor(debug, colMetadata, options) {
|
super({
|
objectMode: true
|
});
|
this.debug = debug;
|
this.colMetadata = colMetadata;
|
this.options = options;
|
this.endOfMessageMarker = {};
|
this.buffer = Buffer.alloc(0);
|
this.position = 0;
|
this.suspended = false;
|
this.next = undefined;
|
}
|
|
_transform(input, encoding, done) {
|
if (input === this.endOfMessageMarker) {
|
done(null, {
|
// generate endOfMessage pseudo token
|
name: 'EOM',
|
event: 'endOfMessage'
|
});
|
return;
|
}
|
|
if (this.position === this.buffer.length) {
|
this.buffer = input;
|
} else {
|
this.buffer = Buffer.concat([this.buffer.slice(this.position), input]);
|
}
|
|
this.position = 0;
|
|
if (this.suspended) {
|
// Unsuspend and continue from where ever we left off.
|
this.suspended = false;
|
this.next.call(null);
|
} // If we're no longer suspended, parse new tokens
|
|
|
if (!this.suspended) {
|
// Start the parser
|
this.parseTokens();
|
}
|
|
done();
|
}
|
|
parseTokens() {
|
const doneParsing = token => {
|
if (token) {
|
switch (token.name) {
|
case 'COLMETADATA':
|
this.colMetadata = token.columns;
|
}
|
|
this.push(token);
|
}
|
};
|
|
while (!this.suspended && this.position + 1 <= this.buffer.length) {
|
const type = this.buffer.readUInt8(this.position);
|
this.position += 1;
|
|
if (tokenParsers[type]) {
|
tokenParsers[type](this, this.colMetadata, this.options, doneParsing);
|
} else {
|
this.emit('error', new Error('Unknown type: ' + type));
|
}
|
}
|
}
|
|
suspend(next) {
|
this.suspended = true;
|
this.next = next;
|
}
|
|
awaitData(length, callback) {
|
if (this.position + length <= this.buffer.length) {
|
callback();
|
} else {
|
this.suspend(() => {
|
this.awaitData(length, callback);
|
});
|
}
|
}
|
|
readInt8(callback) {
|
this.awaitData(1, () => {
|
const data = this.buffer.readInt8(this.position);
|
this.position += 1;
|
callback(data);
|
});
|
}
|
|
readUInt8(callback) {
|
this.awaitData(1, () => {
|
const data = this.buffer.readUInt8(this.position);
|
this.position += 1;
|
callback(data);
|
});
|
}
|
|
readInt16LE(callback) {
|
this.awaitData(2, () => {
|
const data = this.buffer.readInt16LE(this.position);
|
this.position += 2;
|
callback(data);
|
});
|
}
|
|
readInt16BE(callback) {
|
this.awaitData(2, () => {
|
const data = this.buffer.readInt16BE(this.position);
|
this.position += 2;
|
callback(data);
|
});
|
}
|
|
readUInt16LE(callback) {
|
this.awaitData(2, () => {
|
const data = this.buffer.readUInt16LE(this.position);
|
this.position += 2;
|
callback(data);
|
});
|
}
|
|
readUInt16BE(callback) {
|
this.awaitData(2, () => {
|
const data = this.buffer.readUInt16BE(this.position);
|
this.position += 2;
|
callback(data);
|
});
|
}
|
|
readInt32LE(callback) {
|
this.awaitData(4, () => {
|
const data = this.buffer.readInt32LE(this.position);
|
this.position += 4;
|
callback(data);
|
});
|
}
|
|
readInt32BE(callback) {
|
this.awaitData(4, () => {
|
const data = this.buffer.readInt32BE(this.position);
|
this.position += 4;
|
callback(data);
|
});
|
}
|
|
readUInt32LE(callback) {
|
this.awaitData(4, () => {
|
const data = this.buffer.readUInt32LE(this.position);
|
this.position += 4;
|
callback(data);
|
});
|
}
|
|
readUInt32BE(callback) {
|
this.awaitData(4, () => {
|
const data = this.buffer.readUInt32BE(this.position);
|
this.position += 4;
|
callback(data);
|
});
|
}
|
|
readInt64LE(callback) {
|
this.awaitData(8, () => {
|
const data = Math.pow(2, 32) * this.buffer.readInt32LE(this.position + 4) + (this.buffer[this.position + 4] & 0x80 === 0x80 ? 1 : -1) * this.buffer.readUInt32LE(this.position);
|
this.position += 8;
|
callback(data);
|
});
|
}
|
|
readInt64BE(callback) {
|
this.awaitData(8, () => {
|
const data = Math.pow(2, 32) * this.buffer.readInt32BE(this.position) + (this.buffer[this.position] & 0x80 === 0x80 ? 1 : -1) * this.buffer.readUInt32BE(this.position + 4);
|
this.position += 8;
|
callback(data);
|
});
|
}
|
|
readUInt64LE(callback) {
|
this.awaitData(8, () => {
|
const data = Math.pow(2, 32) * this.buffer.readUInt32LE(this.position + 4) + this.buffer.readUInt32LE(this.position);
|
this.position += 8;
|
callback(data);
|
});
|
}
|
|
readUInt64BE(callback) {
|
this.awaitData(8, () => {
|
const data = Math.pow(2, 32) * this.buffer.readUInt32BE(this.position) + this.buffer.readUInt32BE(this.position + 4);
|
this.position += 8;
|
callback(data);
|
});
|
}
|
|
readFloatLE(callback) {
|
this.awaitData(4, () => {
|
const data = this.buffer.readFloatLE(this.position);
|
this.position += 4;
|
callback(data);
|
});
|
}
|
|
readFloatBE(callback) {
|
this.awaitData(4, () => {
|
const data = this.buffer.readFloatBE(this.position);
|
this.position += 4;
|
callback(data);
|
});
|
}
|
|
readDoubleLE(callback) {
|
this.awaitData(8, () => {
|
const data = this.buffer.readDoubleLE(this.position);
|
this.position += 8;
|
callback(data);
|
});
|
}
|
|
readDoubleBE(callback) {
|
this.awaitData(8, () => {
|
const data = this.buffer.readDoubleBE(this.position);
|
this.position += 8;
|
callback(data);
|
});
|
}
|
|
readUInt24LE(callback) {
|
this.awaitData(3, () => {
|
const low = this.buffer.readUInt16LE(this.position);
|
const high = this.buffer.readUInt8(this.position + 2);
|
this.position += 3;
|
callback(low | high << 16);
|
});
|
}
|
|
readUInt40LE(callback) {
|
this.awaitData(5, () => {
|
const low = this.buffer.readUInt32LE(this.position);
|
const high = this.buffer.readUInt8(this.position + 4);
|
this.position += 5;
|
callback(0x100000000 * high + low);
|
});
|
}
|
|
readUNumeric64LE(callback) {
|
this.awaitData(8, () => {
|
const low = this.buffer.readUInt32LE(this.position);
|
const high = this.buffer.readUInt32LE(this.position + 4);
|
this.position += 8;
|
callback(0x100000000 * high + low);
|
});
|
}
|
|
readUNumeric96LE(callback) {
|
this.awaitData(12, () => {
|
const dword1 = this.buffer.readUInt32LE(this.position);
|
const dword2 = this.buffer.readUInt32LE(this.position + 4);
|
const dword3 = this.buffer.readUInt32LE(this.position + 8);
|
this.position += 12;
|
callback(dword1 + 0x100000000 * dword2 + 0x100000000 * 0x100000000 * dword3);
|
});
|
}
|
|
readUNumeric128LE(callback) {
|
this.awaitData(16, () => {
|
const dword1 = this.buffer.readUInt32LE(this.position);
|
const dword2 = this.buffer.readUInt32LE(this.position + 4);
|
const dword3 = this.buffer.readUInt32LE(this.position + 8);
|
const dword4 = this.buffer.readUInt32LE(this.position + 12);
|
this.position += 16;
|
callback(dword1 + 0x100000000 * dword2 + 0x100000000 * 0x100000000 * dword3 + 0x100000000 * 0x100000000 * 0x100000000 * dword4);
|
});
|
} // Variable length data
|
|
|
readBuffer(length, callback) {
|
this.awaitData(length, () => {
|
const data = this.buffer.slice(this.position, this.position + length);
|
this.position += length;
|
callback(data);
|
});
|
} // Read a Unicode String (BVARCHAR)
|
|
|
readBVarChar(callback) {
|
this.readUInt8(length => {
|
this.readBuffer(length * 2, data => {
|
callback(data.toString('ucs2'));
|
});
|
});
|
} // Read a Unicode String (USVARCHAR)
|
|
|
readUsVarChar(callback) {
|
this.readUInt16LE(length => {
|
this.readBuffer(length * 2, data => {
|
callback(data.toString('ucs2'));
|
});
|
});
|
} // Read binary data (BVARBYTE)
|
|
|
readBVarByte(callback) {
|
this.readUInt8(length => {
|
this.readBuffer(length, callback);
|
});
|
} // Read binary data (USVARBYTE)
|
|
|
readUsVarByte(callback) {
|
this.readUInt16LE(length => {
|
this.readBuffer(length, callback);
|
});
|
}
|
|
};
|