Skip to content

Commit

Permalink
fix: switch to jsbi for reading and writing int64 and uint64 va…
Browse files Browse the repository at this point in the history
…lues
  • Loading branch information
arthurschreiber committed Oct 2, 2019
1 parent 717eb95 commit e666077
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 266 deletions.
14 changes: 12 additions & 2 deletions src/token/done-token-parser.ts
@@ -1,3 +1,5 @@
import JSBI from 'jsbi';

import Parser from './stream-parser';
import { ColumnMetadata } from './colmetadata-token-parser';
import { InternalConnectionOptions } from '../connection';
Expand Down Expand Up @@ -33,7 +35,7 @@ function parseToken(parser: Parser, options: InternalConnectionOptions, callback
const serverError = !!(status & STATUS.SRVERROR);

parser.readUInt16LE((curCmd) => {
(options.tdsVersion < '7_2' ? parser.readUInt32LE : parser.readUInt64LE).call(parser, (rowCount) => {
const next = (rowCount: number) => {
callback({
more: more,
sqlError: sqlError,
Expand All @@ -42,7 +44,15 @@ function parseToken(parser: Parser, options: InternalConnectionOptions, callback
rowCount: rowCountValid ? rowCount : undefined,
curCmd: curCmd
});
});
};

if (options.tdsVersion < '7_2') {
parser.readUInt32LE(next);
} else {
parser.readBigUInt64LE((rowCount) => {
next(JSBI.toNumber(rowCount));
});
}
});
});
}
Expand Down
38 changes: 38 additions & 0 deletions src/token/stream-parser.ts
@@ -1,5 +1,6 @@
import Debug from '../debug';
import { InternalConnectionOptions } from '../connection';
import JSBI from 'jsbi';

const Transform = require('readable-stream').Transform;
import { TYPE, Token, EndOfMessageToken, ColMetadataToken } from './token';
Expand Down Expand Up @@ -212,6 +213,32 @@ class Parser extends Transform {
});
}

readBigInt64LE(callback: (data: JSBI) => void) {
this.awaitData(8, () => {
const result = JSBI.add(
JSBI.leftShift(
JSBI.BigInt(
this.buffer[this.position + 4] +
this.buffer[this.position + 5] * 2 ** 8 +
this.buffer[this.position + 6] * 2 ** 16 +
(this.buffer[this.position + 7] << 24) // Overflow
),
JSBI.BigInt(32)
),
JSBI.BigInt(
this.buffer[this.position] +
this.buffer[this.position + 1] * 2 ** 8 +
this.buffer[this.position + 2] * 2 ** 16 +
this.buffer[this.position + 3] * 2 ** 24
)
);

this.position += 8;

callback(result);
});
}

readInt64LE(callback: (data: number) => void) {
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);
Expand All @@ -228,6 +255,17 @@ class Parser extends Transform {
});
}

readBigUInt64LE(callback: (data: JSBI) => void) {
this.awaitData(8, () => {
const low = JSBI.BigInt(this.buffer.readUInt32LE(this.position));
const high = JSBI.BigInt(this.buffer.readUInt32LE(this.position + 4));

this.position += 8;

callback(JSBI.add(low, JSBI.leftShift(high, JSBI.BigInt(32))));
});
}

readUInt64LE(callback: (data: number) => void) {
this.awaitData(8, () => {
const data = Math.pow(2, 32) * this.buffer.readUInt32LE(this.position + 4) + this.buffer.readUInt32LE(this.position);
Expand Down
83 changes: 0 additions & 83 deletions src/tracking-buffer/bigint.ts

This file was deleted.

40 changes: 35 additions & 5 deletions src/tracking-buffer/writable-tracking-buffer.ts
@@ -1,4 +1,4 @@
import { numberToInt64LE } from './bigint';
import JSBI from 'jsbi';

const SHIFT_LEFT_32 = (1 << 16) * (1 << 16);
const SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;
Expand Down Expand Up @@ -106,9 +106,36 @@ class WritableTrackingBuffer {
this.position += length;
}

writeBigInt64LE(value: JSBI) {
this.writeBigU_Int64LE(value);
}

private writeBigU_Int64LE(value: JSBI) {
this.makeRoomFor(8);

let lo = JSBI.toNumber(JSBI.bitwiseAnd(value, JSBI.BigInt(0xffffffff)));

this.buffer[this.position++] = lo;
lo = lo >> 8;
this.buffer[this.position++] = lo;
lo = lo >> 8;
this.buffer[this.position++] = lo;
lo = lo >> 8;
this.buffer[this.position++] = lo;

let hi = JSBI.toNumber(JSBI.bitwiseAnd(JSBI.signedRightShift(value, JSBI.BigInt(32)), JSBI.BigInt(0xffffffff)));

this.buffer[this.position++] = hi;
hi = hi >> 8;
this.buffer[this.position++] = hi;
hi = hi >> 8;
this.buffer[this.position++] = hi;
hi = hi >> 8;
this.buffer[this.position++] = hi;
}

writeInt64LE(value: number) {
const buf = numberToInt64LE(value);
this.copyFrom(buf);
this.writeBigInt64LE(JSBI.BigInt(value));
}

writeUInt32BE(value: number) {
Expand All @@ -125,8 +152,11 @@ class WritableTrackingBuffer {
}

writeUInt64LE(value: number) {
this.writeInt32LE(value & -1);
this.writeUInt32LE(Math.floor(value * SHIFT_RIGHT_32));
this.writeBigUInt64LE(JSBI.BigInt(value));
}

writeBigUInt64LE(value: JSBI) {
this.writeBigU_Int64LE(value);
}

writeInt8(value: number) {
Expand Down
9 changes: 4 additions & 5 deletions src/value-parser.js
Expand Up @@ -6,7 +6,6 @@ const guidParser = require('./guid-parser');
const readPrecision = require('./metadata-parser').readPrecision;
const readScale = require('./metadata-parser').readScale;
const readCollation = require('./metadata-parser').readCollation;
const convertLEBytesToString = require('./tracking-buffer/bigint').convertLEBytesToString;

const NULL = (1 << 16) - 1;
const MAX = (1 << 16) - 1;
Expand Down Expand Up @@ -87,8 +86,8 @@ function valueParse(parser, metaData, options, callback) {
return parser.readInt16LE(callback);

case 'BigInt':
return parser.readBuffer(8, (buffer) => {
callback(convertLEBytesToString(buffer));
return parser.readBigInt64LE((value) => {
callback(value.toString());
});

case 'IntN':
Expand All @@ -103,8 +102,8 @@ function valueParse(parser, metaData, options, callback) {
case 4:
return parser.readInt32LE(callback);
case 8:
return parser.readBuffer(8, (buffer) => {
callback(convertLEBytesToString(buffer));
return parser.readBigInt64LE((value) => {
callback(value.toString());
});

default:
Expand Down
4 changes: 4 additions & 0 deletions test/integration/datatypes-in-results-test.js
Expand Up @@ -127,6 +127,10 @@ describe('Datatypes in results test', function() {
execSql(done, 'select cast(8 as bigint)', '8');
});

it('should test negative big int', function(done) {
execSql(done, 'select cast(-8 as bigint)', '-8');
});

it('should test big int null', function(done) {
execSql(done, 'select cast(null as bigint)', null);
});
Expand Down
12 changes: 12 additions & 0 deletions test/integration/rpc-test.js
Expand Up @@ -201,6 +201,18 @@ describe('RPC test', function() {
testProc(done, TYPES.SmallInt, 'smallint', null);
});

it('should exec proc bigint', function(done) {
testProc(done, TYPES.BigInt, 'bigint', '3');
});

it('should exec proc negative bigint', function(done) {
testProc(done, TYPES.BigInt, 'bigint', '-3');
});

it('should exec proc bigint null', function(done) {
testProc(done, TYPES.BigInt, 'bigint', null);
});

it('should exec proc int', function(done) {
testProc(done, TYPES.Int, 'int', 3);
});
Expand Down

0 comments on commit e666077

Please sign in to comment.