Skip to content

Commit

Permalink
feat: use jsbi for reading and writing int64 and uint64 values
Browse files Browse the repository at this point in the history
  • Loading branch information
arthurschreiber committed Oct 15, 2019
1 parent c60eaef commit 1a740aa
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 265 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
6 changes: 2 additions & 4 deletions src/value-parser.ts
Expand Up @@ -7,8 +7,6 @@ const iconv = require('iconv-lite');
const sprintf = require('sprintf-js').sprintf;
import { bufferToLowerCaseGuid, bufferToUpperCaseGuid } from './guid-parser';

const convertLEBytesToString = require('./tracking-buffer/bigint').convertLEBytesToString;

const NULL = (1 << 16) - 1;
const MAX = (1 << 16) - 1;
const THREE_AND_A_THIRD = 3 + (1 / 3);
Expand All @@ -30,8 +28,8 @@ function readInt(parser: Parser, callback: (value: unknown) => void) {
}

function readBigInt(parser: Parser, callback: (value: unknown) => void) {
parser.readBuffer(8, (buffer) => {
callback(convertLEBytesToString(buffer));
parser.readBigInt64LE((value) => {
callback(value.toString());
});
}

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 1a740aa

Please sign in to comment.