Skip to content

Commit 1a740aa

Browse files
feat: use jsbi for reading and writing int64 and uint64 values
1 parent c60eaef commit 1a740aa

File tree

9 files changed

+120
-265
lines changed

9 files changed

+120
-265
lines changed

src/token/done-token-parser.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import JSBI from 'jsbi';
2+
13
import Parser from './stream-parser';
24
import { ColumnMetadata } from './colmetadata-token-parser';
35
import { InternalConnectionOptions } from '../connection';
@@ -33,7 +35,7 @@ function parseToken(parser: Parser, options: InternalConnectionOptions, callback
3335
const serverError = !!(status & STATUS.SRVERROR);
3436

3537
parser.readUInt16LE((curCmd) => {
36-
(options.tdsVersion < '7_2' ? parser.readUInt32LE : parser.readUInt64LE).call(parser, (rowCount) => {
38+
const next = (rowCount: number) => {
3739
callback({
3840
more: more,
3941
sqlError: sqlError,
@@ -42,7 +44,15 @@ function parseToken(parser: Parser, options: InternalConnectionOptions, callback
4244
rowCount: rowCountValid ? rowCount : undefined,
4345
curCmd: curCmd
4446
});
45-
});
47+
};
48+
49+
if (options.tdsVersion < '7_2') {
50+
parser.readUInt32LE(next);
51+
} else {
52+
parser.readBigUInt64LE((rowCount) => {
53+
next(JSBI.toNumber(rowCount));
54+
});
55+
}
4656
});
4757
});
4858
}

src/token/stream-parser.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Debug from '../debug';
22
import { InternalConnectionOptions } from '../connection';
3+
import JSBI from 'jsbi';
34

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

216+
readBigInt64LE(callback: (data: JSBI) => void) {
217+
this.awaitData(8, () => {
218+
const result = JSBI.add(
219+
JSBI.leftShift(
220+
JSBI.BigInt(
221+
this.buffer[this.position + 4] +
222+
this.buffer[this.position + 5] * 2 ** 8 +
223+
this.buffer[this.position + 6] * 2 ** 16 +
224+
(this.buffer[this.position + 7] << 24) // Overflow
225+
),
226+
JSBI.BigInt(32)
227+
),
228+
JSBI.BigInt(
229+
this.buffer[this.position] +
230+
this.buffer[this.position + 1] * 2 ** 8 +
231+
this.buffer[this.position + 2] * 2 ** 16 +
232+
this.buffer[this.position + 3] * 2 ** 24
233+
)
234+
);
235+
236+
this.position += 8;
237+
238+
callback(result);
239+
});
240+
}
241+
215242
readInt64LE(callback: (data: number) => void) {
216243
this.awaitData(8, () => {
217244
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);
@@ -228,6 +255,17 @@ class Parser extends Transform {
228255
});
229256
}
230257

258+
readBigUInt64LE(callback: (data: JSBI) => void) {
259+
this.awaitData(8, () => {
260+
const low = JSBI.BigInt(this.buffer.readUInt32LE(this.position));
261+
const high = JSBI.BigInt(this.buffer.readUInt32LE(this.position + 4));
262+
263+
this.position += 8;
264+
265+
callback(JSBI.add(low, JSBI.leftShift(high, JSBI.BigInt(32))));
266+
});
267+
}
268+
231269
readUInt64LE(callback: (data: number) => void) {
232270
this.awaitData(8, () => {
233271
const data = Math.pow(2, 32) * this.buffer.readUInt32LE(this.position + 4) + this.buffer.readUInt32LE(this.position);

src/tracking-buffer/bigint.ts

Lines changed: 0 additions & 83 deletions
This file was deleted.

src/tracking-buffer/writable-tracking-buffer.ts

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { numberToInt64LE } from './bigint';
1+
import JSBI from 'jsbi';
22

33
const SHIFT_LEFT_32 = (1 << 16) * (1 << 16);
44
const SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;
@@ -106,9 +106,36 @@ class WritableTrackingBuffer {
106106
this.position += length;
107107
}
108108

109+
writeBigInt64LE(value: JSBI) {
110+
this.writeBigU_Int64LE(value);
111+
}
112+
113+
private writeBigU_Int64LE(value: JSBI) {
114+
this.makeRoomFor(8);
115+
116+
let lo = JSBI.toNumber(JSBI.bitwiseAnd(value, JSBI.BigInt(0xffffffff)));
117+
118+
this.buffer[this.position++] = lo;
119+
lo = lo >> 8;
120+
this.buffer[this.position++] = lo;
121+
lo = lo >> 8;
122+
this.buffer[this.position++] = lo;
123+
lo = lo >> 8;
124+
this.buffer[this.position++] = lo;
125+
126+
let hi = JSBI.toNumber(JSBI.bitwiseAnd(JSBI.signedRightShift(value, JSBI.BigInt(32)), JSBI.BigInt(0xffffffff)));
127+
128+
this.buffer[this.position++] = hi;
129+
hi = hi >> 8;
130+
this.buffer[this.position++] = hi;
131+
hi = hi >> 8;
132+
this.buffer[this.position++] = hi;
133+
hi = hi >> 8;
134+
this.buffer[this.position++] = hi;
135+
}
136+
109137
writeInt64LE(value: number) {
110-
const buf = numberToInt64LE(value);
111-
this.copyFrom(buf);
138+
this.writeBigInt64LE(JSBI.BigInt(value));
112139
}
113140

114141
writeUInt32BE(value: number) {
@@ -125,8 +152,11 @@ class WritableTrackingBuffer {
125152
}
126153

127154
writeUInt64LE(value: number) {
128-
this.writeInt32LE(value & -1);
129-
this.writeUInt32LE(Math.floor(value * SHIFT_RIGHT_32));
155+
this.writeBigUInt64LE(JSBI.BigInt(value));
156+
}
157+
158+
writeBigUInt64LE(value: JSBI) {
159+
this.writeBigU_Int64LE(value);
130160
}
131161

132162
writeInt8(value: number) {

src/value-parser.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ const iconv = require('iconv-lite');
77
const sprintf = require('sprintf-js').sprintf;
88
import { bufferToLowerCaseGuid, bufferToUpperCaseGuid } from './guid-parser';
99

10-
const convertLEBytesToString = require('./tracking-buffer/bigint').convertLEBytesToString;
11-
1210
const NULL = (1 << 16) - 1;
1311
const MAX = (1 << 16) - 1;
1412
const THREE_AND_A_THIRD = 3 + (1 / 3);
@@ -30,8 +28,8 @@ function readInt(parser: Parser, callback: (value: unknown) => void) {
3028
}
3129

3230
function readBigInt(parser: Parser, callback: (value: unknown) => void) {
33-
parser.readBuffer(8, (buffer) => {
34-
callback(convertLEBytesToString(buffer));
31+
parser.readBigInt64LE((value) => {
32+
callback(value.toString());
3533
});
3634
}
3735

test/integration/datatypes-in-results-test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ describe('Datatypes in results test', function() {
127127
execSql(done, 'select cast(8 as bigint)', '8');
128128
});
129129

130+
it('should test negative big int', function(done) {
131+
execSql(done, 'select cast(-8 as bigint)', '-8');
132+
});
133+
130134
it('should test big int null', function(done) {
131135
execSql(done, 'select cast(null as bigint)', null);
132136
});

test/integration/rpc-test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,18 @@ describe('RPC test', function() {
201201
testProc(done, TYPES.SmallInt, 'smallint', null);
202202
});
203203

204+
it('should exec proc bigint', function(done) {
205+
testProc(done, TYPES.BigInt, 'bigint', '3');
206+
});
207+
208+
it('should exec proc negative bigint', function(done) {
209+
testProc(done, TYPES.BigInt, 'bigint', '-3');
210+
});
211+
212+
it('should exec proc bigint null', function(done) {
213+
testProc(done, TYPES.BigInt, 'bigint', null);
214+
});
215+
204216
it('should exec proc int', function(done) {
205217
testProc(done, TYPES.Int, 'int', 3);
206218
});

0 commit comments

Comments
 (0)