node-firebird 2.0.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/node.js.yml +20 -0
- package/CI_DEBUGGING_GUIDE.md +148 -0
- package/FIREBIRD_LOG_FEATURE.md +145 -0
- package/MINIMAL_CHANGES_SUMMARY.md +136 -0
- package/PR_SUMMARY.md +88 -131
- package/README.md +142 -22
- package/ROADMAP.md +223 -0
- package/SRP_PROTOCOL.md +480 -0
- package/lib/ieee754-decimal.js +500 -0
- package/lib/index.d.ts +10 -6
- package/lib/wire/connection.js +263 -61
- package/lib/wire/const.js +29 -0
- package/lib/wire/database.js +78 -9
- package/lib/wire/eventConnection.js +6 -3
- package/lib/wire/fbEventManager.js +234 -41
- package/lib/wire/serialize.js +39 -0
- package/lib/wire/service.js +102 -94
- package/lib/wire/statement.js +4 -4
- package/lib/wire/transaction.js +27 -9
- package/lib/wire/xsqlvar.js +176 -0
- package/package.json +5 -4
- package/vitest.config.js +12 -1
- package/0001-fix-attachEvent-host-resolution.patch +0 -72
- package/Roadmap.md +0 -80
- package/diff +0 -62
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IEEE 754-2008 Decimal Floating Point Support
|
|
3
|
+
*
|
|
4
|
+
* This module implements encoding and decoding of IEEE 754 Decimal64 and Decimal128
|
|
5
|
+
* formats using the BID (Binary Integer Decimal) encoding.
|
|
6
|
+
*
|
|
7
|
+
* Based on the decimal-java library from FirebirdSQL:
|
|
8
|
+
* https://github.com/FirebirdSQL/decimal-java
|
|
9
|
+
*
|
|
10
|
+
* References:
|
|
11
|
+
* - IEEE 754-2008 Standard
|
|
12
|
+
* - https://en.wikipedia.org/wiki/Decimal64_floating-point_format
|
|
13
|
+
* - https://en.wikipedia.org/wiki/Decimal128_floating-point_format
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// IEEE 754 Decimal64 constants (16 decimal digits of precision)
|
|
17
|
+
const DECIMAL64_BIAS = 398;
|
|
18
|
+
const DECIMAL64_MAX_EXPONENT = 369;
|
|
19
|
+
const DECIMAL64_MIN_EXPONENT = -398;
|
|
20
|
+
const DECIMAL64_MAX_COEFFICIENT = 9999999999999999n; // 16 digits
|
|
21
|
+
|
|
22
|
+
// IEEE 754 Decimal128 constants (34 decimal digits of precision)
|
|
23
|
+
const DECIMAL128_BIAS = 6176;
|
|
24
|
+
const DECIMAL128_MAX_EXPONENT = 6111;
|
|
25
|
+
const DECIMAL128_MIN_EXPONENT = -6176;
|
|
26
|
+
const DECIMAL128_MAX_COEFFICIENT = 9999999999999999999999999999999999n; // 34 digits
|
|
27
|
+
|
|
28
|
+
// Special value patterns
|
|
29
|
+
const DECIMAL64_INFINITY = 0x7800000000000000n;
|
|
30
|
+
const DECIMAL64_NEG_INFINITY = 0xF800000000000000n;
|
|
31
|
+
const DECIMAL64_NAN = 0x7C00000000000000n;
|
|
32
|
+
const DECIMAL64_SNAN = 0x7E00000000000000n;
|
|
33
|
+
|
|
34
|
+
const DECIMAL128_INFINITY_HIGH = 0x7800000000000000n;
|
|
35
|
+
const DECIMAL128_NEG_INFINITY_HIGH = 0xF800000000000000n;
|
|
36
|
+
const DECIMAL128_NAN_HIGH = 0x7C00000000000000n;
|
|
37
|
+
const DECIMAL128_SNAN_HIGH = 0x7E00000000000000n;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Encode a number to IEEE 754 Decimal64 format (8 bytes)
|
|
41
|
+
* @param {number|string|BigInt} value - The value to encode
|
|
42
|
+
* @returns {Buffer} - 8-byte buffer containing the Decimal64 encoding
|
|
43
|
+
*/
|
|
44
|
+
function encodeDecimal64(value) {
|
|
45
|
+
// Handle special cases
|
|
46
|
+
if (value === null || value === undefined) {
|
|
47
|
+
return Buffer.alloc(8);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (typeof value === 'number') {
|
|
51
|
+
if (isNaN(value)) {
|
|
52
|
+
return bigIntToBuffer(DECIMAL64_NAN, 8);
|
|
53
|
+
}
|
|
54
|
+
if (!isFinite(value)) {
|
|
55
|
+
return bigIntToBuffer(value > 0 ? DECIMAL64_INFINITY : DECIMAL64_NEG_INFINITY, 8);
|
|
56
|
+
}
|
|
57
|
+
value = value.toString();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (Buffer.isBuffer(value)) {
|
|
61
|
+
return value.slice(0, 8);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Parse the decimal string
|
|
65
|
+
const str = value.toString();
|
|
66
|
+
const sign = str.startsWith('-') ? 1n : 0n;
|
|
67
|
+
const absStr = str.replace(/^-/, '');
|
|
68
|
+
|
|
69
|
+
// Handle zero
|
|
70
|
+
if (parseFloat(absStr) === 0) {
|
|
71
|
+
return bigIntToBuffer(sign << 63n, 8);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Parse coefficient and exponent
|
|
75
|
+
let coefficient, exponent;
|
|
76
|
+
const eIndex = absStr.toLowerCase().indexOf('e');
|
|
77
|
+
|
|
78
|
+
if (eIndex !== -1) {
|
|
79
|
+
const mantissa = absStr.substring(0, eIndex).replace('.', '');
|
|
80
|
+
coefficient = mantissa === '' || mantissa === '-' ? 0n : BigInt(mantissa);
|
|
81
|
+
const expPart = absStr.substring(eIndex + 1);
|
|
82
|
+
const dotIndex = absStr.indexOf('.');
|
|
83
|
+
if (dotIndex !== -1 && dotIndex < eIndex) {
|
|
84
|
+
exponent = parseInt(expPart) - (eIndex - dotIndex - 1);
|
|
85
|
+
} else {
|
|
86
|
+
exponent = parseInt(expPart);
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
const dotIndex = absStr.indexOf('.');
|
|
90
|
+
if (dotIndex !== -1) {
|
|
91
|
+
const withoutDot = absStr.replace('.', '');
|
|
92
|
+
coefficient = withoutDot === '' || withoutDot === '-' ? 0n : BigInt(withoutDot);
|
|
93
|
+
exponent = -(absStr.length - dotIndex - 1);
|
|
94
|
+
} else {
|
|
95
|
+
coefficient = absStr === '' || absStr === '-' ? 0n : BigInt(absStr);
|
|
96
|
+
exponent = 0;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Normalize: remove trailing zeros
|
|
101
|
+
while (coefficient % 10n === 0n && coefficient !== 0n && exponent < DECIMAL64_MAX_EXPONENT) {
|
|
102
|
+
coefficient /= 10n;
|
|
103
|
+
exponent++;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Check coefficient range
|
|
107
|
+
if (coefficient > DECIMAL64_MAX_COEFFICIENT) {
|
|
108
|
+
throw new Error(`Coefficient ${coefficient} exceeds Decimal64 maximum`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Adjust exponent with bias
|
|
112
|
+
const biasedExponent = exponent + DECIMAL64_BIAS;
|
|
113
|
+
if (biasedExponent < 0 || biasedExponent > 767) {
|
|
114
|
+
throw new Error(`Exponent ${exponent} out of Decimal64 range`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Encode using BID (Binary Integer Decimal) format
|
|
118
|
+
// Bit layout: S(1) | Combination(5) | Exponent continuation(8) | Coefficient continuation(50)
|
|
119
|
+
|
|
120
|
+
let encoded = 0n;
|
|
121
|
+
|
|
122
|
+
// Sign bit (bit 63)
|
|
123
|
+
encoded |= sign << 63n;
|
|
124
|
+
|
|
125
|
+
// Split coefficient into MSD and continuation
|
|
126
|
+
const msd = Number(coefficient / 1000000000000000n); // Most significant digit
|
|
127
|
+
const coeffCont = coefficient % 1000000000000000n; // Lower 15 digits (50 bits max)
|
|
128
|
+
|
|
129
|
+
const expBigInt = BigInt(biasedExponent);
|
|
130
|
+
const expTop = (expBigInt >> 8n) & 0x3n; // Top 2 bits of exponent (bits 9-8)
|
|
131
|
+
const expLow = expBigInt & 0xFFn; // Lower 8 bits of exponent (bits 7-0)
|
|
132
|
+
|
|
133
|
+
if (msd <= 7) {
|
|
134
|
+
// Combination: G0 G1 G2 G3 G4 where G0 G1 = expTop, G2 G3 G4 = MSD
|
|
135
|
+
const combo = (expTop << 3n) | BigInt(msd);
|
|
136
|
+
encoded |= combo << 58n;
|
|
137
|
+
// Exponent continuation (8 bits at position 57-50)
|
|
138
|
+
encoded |= expLow << 50n;
|
|
139
|
+
// Coefficient continuation (50 bits at position 49-0)
|
|
140
|
+
encoded |= coeffCont & 0x3FFFFFFFFFFFFn;
|
|
141
|
+
} else {
|
|
142
|
+
// Combination: 11 G2 G3 G4 where G2 = (MSD-8), G3 G4 = expTop
|
|
143
|
+
const combo = 0x18n | ((BigInt(msd - 8) & 0x1n) << 2n) | expTop;
|
|
144
|
+
encoded |= combo << 58n;
|
|
145
|
+
// Exponent continuation (8 bits at position 57-50)
|
|
146
|
+
encoded |= expLow << 50n;
|
|
147
|
+
// Coefficient continuation (50 bits at position 49-0)
|
|
148
|
+
encoded |= coeffCont & 0x3FFFFFFFFFFFFn;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return bigIntToBuffer(encoded, 8);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Decode IEEE 754 Decimal64 format (8 bytes) to a string
|
|
156
|
+
* @param {Buffer} buffer - 8-byte buffer containing the Decimal64 encoding
|
|
157
|
+
* @returns {string|number} - Decoded value as string or special value
|
|
158
|
+
*/
|
|
159
|
+
function decodeDecimal64(buffer) {
|
|
160
|
+
if (buffer.length !== 8) {
|
|
161
|
+
throw new Error('Decimal64 buffer must be 8 bytes');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const encoded = bufferToBigInt(buffer);
|
|
165
|
+
|
|
166
|
+
// Extract sign
|
|
167
|
+
const sign = (encoded >> 63n) & 0x1n;
|
|
168
|
+
|
|
169
|
+
// Extract combination field (bits 62-58, 5 bits)
|
|
170
|
+
const combo = (encoded >> 58n) & 0x1Fn;
|
|
171
|
+
|
|
172
|
+
if ((combo & 0x1En) === 0x1En) {
|
|
173
|
+
// Special value (NaN or Infinity)
|
|
174
|
+
if ((combo & 0x1n) === 0n) {
|
|
175
|
+
return sign ? -Infinity : Infinity;
|
|
176
|
+
} else {
|
|
177
|
+
return NaN;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Decode exponent and coefficient
|
|
182
|
+
let exponent, coefficient, msd;
|
|
183
|
+
|
|
184
|
+
if ((combo & 0x18n) !== 0x18n) {
|
|
185
|
+
// Combination: G0 G1 G2 G3 G4 where G0 G1 != 11
|
|
186
|
+
// MSD is G2 G3 G4 (bits 2-0 of combo)
|
|
187
|
+
msd = Number(combo & 0x7n);
|
|
188
|
+
// Exponent top 2 bits are G0 G1 (bits 4-3 of combo)
|
|
189
|
+
const expTop = (combo >> 3n) & 0x3n;
|
|
190
|
+
// Exponent continuation is bits 57-50 (8 bits)
|
|
191
|
+
const expLow = (encoded >> 50n) & 0xFFn;
|
|
192
|
+
exponent = (expTop << 8n) | expLow;
|
|
193
|
+
// Coefficient continuation is bits 49-0 (50 bits)
|
|
194
|
+
const coeffCont = encoded & 0x3FFFFFFFFFFFFn;
|
|
195
|
+
coefficient = BigInt(msd) * 1000000000000000n + coeffCont;
|
|
196
|
+
} else {
|
|
197
|
+
// Combination: 11 G2 G3 G4 (MSD 8-9)
|
|
198
|
+
// MSD is 8 + G2 (bit 2 of combo)
|
|
199
|
+
msd = 8 + Number((combo >> 2n) & 0x1n);
|
|
200
|
+
// Exponent top 2 bits are G3 G4 (bits 1-0 of combo)
|
|
201
|
+
const expTop = combo & 0x3n;
|
|
202
|
+
// Exponent continuation is bits 57-50 (8 bits)
|
|
203
|
+
const expLow = (encoded >> 50n) & 0xFFn;
|
|
204
|
+
exponent = (expTop << 8n) | expLow;
|
|
205
|
+
// Coefficient continuation is bits 49-0 (50 bits)
|
|
206
|
+
const coeffCont = encoded & 0x3FFFFFFFFFFFFn;
|
|
207
|
+
coefficient = BigInt(msd) * 1000000000000000n + coeffCont;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Remove bias from exponent
|
|
211
|
+
const unbias = Number(exponent) - DECIMAL64_BIAS;
|
|
212
|
+
|
|
213
|
+
// Build result string
|
|
214
|
+
const coeffStr = coefficient.toString();
|
|
215
|
+
const signStr = sign ? '-' : '';
|
|
216
|
+
|
|
217
|
+
// Special case: if coefficient is 0, just return "0" regardless of exponent
|
|
218
|
+
if (coefficient === 0n) {
|
|
219
|
+
return signStr + '0';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (unbias === 0) {
|
|
223
|
+
return signStr + coeffStr;
|
|
224
|
+
} else if (unbias > 0) {
|
|
225
|
+
return signStr + coeffStr + '0'.repeat(unbias);
|
|
226
|
+
} else {
|
|
227
|
+
const absExp = -unbias;
|
|
228
|
+
if (absExp >= coeffStr.length) {
|
|
229
|
+
return signStr + '0.' + '0'.repeat(absExp - coeffStr.length) + coeffStr;
|
|
230
|
+
} else {
|
|
231
|
+
const intPart = coeffStr.substring(0, coeffStr.length - absExp);
|
|
232
|
+
const fracPart = coeffStr.substring(coeffStr.length - absExp);
|
|
233
|
+
return signStr + intPart + '.' + fracPart;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Encode a number to IEEE 754 Decimal128 format (16 bytes)
|
|
240
|
+
* @param {number|string|BigInt} value - The value to encode
|
|
241
|
+
* @returns {Buffer} - 16-byte buffer containing the Decimal128 encoding
|
|
242
|
+
*/
|
|
243
|
+
function encodeDecimal128(value) {
|
|
244
|
+
// Handle special cases
|
|
245
|
+
if (value === null || value === undefined) {
|
|
246
|
+
return Buffer.alloc(16);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (typeof value === 'number') {
|
|
250
|
+
if (isNaN(value)) {
|
|
251
|
+
const buf = Buffer.alloc(16);
|
|
252
|
+
bufferWriteBigInt(buf, DECIMAL128_NAN_HIGH, 0, 8);
|
|
253
|
+
return buf;
|
|
254
|
+
}
|
|
255
|
+
if (!isFinite(value)) {
|
|
256
|
+
const buf = Buffer.alloc(16);
|
|
257
|
+
bufferWriteBigInt(buf, value > 0 ? DECIMAL128_INFINITY_HIGH : DECIMAL128_NEG_INFINITY_HIGH, 0, 8);
|
|
258
|
+
return buf;
|
|
259
|
+
}
|
|
260
|
+
value = value.toString();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (Buffer.isBuffer(value)) {
|
|
264
|
+
return value.slice(0, 16);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Parse the decimal string
|
|
268
|
+
const str = value.toString();
|
|
269
|
+
const sign = str.startsWith('-') ? 1n : 0n;
|
|
270
|
+
const absStr = str.replace(/^-/, '');
|
|
271
|
+
|
|
272
|
+
// Handle zero
|
|
273
|
+
if (parseFloat(absStr) === 0) {
|
|
274
|
+
const buf = Buffer.alloc(16);
|
|
275
|
+
bufferWriteBigInt(buf, sign << 63n, 0, 8);
|
|
276
|
+
return buf;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Parse coefficient and exponent
|
|
280
|
+
let coefficient, exponent;
|
|
281
|
+
const eIndex = absStr.toLowerCase().indexOf('e');
|
|
282
|
+
|
|
283
|
+
if (eIndex !== -1) {
|
|
284
|
+
const mantissa = absStr.substring(0, eIndex).replace('.', '');
|
|
285
|
+
coefficient = mantissa === '' || mantissa === '-' ? 0n : BigInt(mantissa);
|
|
286
|
+
const expPart = absStr.substring(eIndex + 1);
|
|
287
|
+
const dotIndex = absStr.indexOf('.');
|
|
288
|
+
if (dotIndex !== -1 && dotIndex < eIndex) {
|
|
289
|
+
exponent = parseInt(expPart) - (eIndex - dotIndex - 1);
|
|
290
|
+
} else {
|
|
291
|
+
exponent = parseInt(expPart);
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
294
|
+
const dotIndex = absStr.indexOf('.');
|
|
295
|
+
if (dotIndex !== -1) {
|
|
296
|
+
const withoutDot = absStr.replace('.', '');
|
|
297
|
+
coefficient = withoutDot === '' || withoutDot === '-' ? 0n : BigInt(withoutDot);
|
|
298
|
+
exponent = -(absStr.length - dotIndex - 1);
|
|
299
|
+
} else {
|
|
300
|
+
coefficient = absStr === '' || absStr === '-' ? 0n : BigInt(absStr);
|
|
301
|
+
exponent = 0;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Normalize: remove trailing zeros
|
|
306
|
+
while (coefficient % 10n === 0n && coefficient !== 0n && exponent < DECIMAL128_MAX_EXPONENT) {
|
|
307
|
+
coefficient /= 10n;
|
|
308
|
+
exponent++;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Check coefficient range
|
|
312
|
+
if (coefficient > DECIMAL128_MAX_COEFFICIENT) {
|
|
313
|
+
throw new Error(`Coefficient ${coefficient} exceeds Decimal128 maximum`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Adjust exponent with bias
|
|
317
|
+
const biasedExponent = exponent + DECIMAL128_BIAS;
|
|
318
|
+
if (biasedExponent < 0 || biasedExponent > 12287) {
|
|
319
|
+
throw new Error(`Exponent ${exponent} out of Decimal128 range`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Encode using BID format
|
|
323
|
+
// Bit layout: S(1) | Combination(5) | Exponent continuation(12) | Coefficient continuation(110)
|
|
324
|
+
|
|
325
|
+
const expBigInt = BigInt(biasedExponent);
|
|
326
|
+
|
|
327
|
+
// Split coefficient into MSD and continuation
|
|
328
|
+
const msd = Number(coefficient / 1000000000000000000000000000000000n); // Most significant digit (10^33)
|
|
329
|
+
const coeffCont = coefficient % 1000000000000000000000000000000000n; // Lower 33 digits (110 bits max)
|
|
330
|
+
|
|
331
|
+
let high = 0n;
|
|
332
|
+
let low = coeffCont & ((1n << 64n) - 1n); // Lower 64 bits of coefficient
|
|
333
|
+
|
|
334
|
+
// Sign bit (bit 127)
|
|
335
|
+
high |= sign << 63n;
|
|
336
|
+
|
|
337
|
+
const expTop = (expBigInt >> 12n) & 0x3n; // Top 2 bits of exponent (bits 13-12)
|
|
338
|
+
const expLow = expBigInt & 0xFFFn; // Lower 12 bits of exponent (bits 11-0)
|
|
339
|
+
|
|
340
|
+
if (msd <= 7) {
|
|
341
|
+
// Combination: G0 G1 G2 G3 G4 where G0 G1 = expTop, G2 G3 G4 = MSD
|
|
342
|
+
const combo = (expTop << 3n) | BigInt(msd);
|
|
343
|
+
high |= combo << 58n;
|
|
344
|
+
// Exponent continuation (12 bits at position 121-110, which is bits 57-46 in high)
|
|
345
|
+
high |= expLow << 46n;
|
|
346
|
+
// Coefficient continuation high 46 bits (bits 109-64, which is bits 45-0 in high)
|
|
347
|
+
high |= (coeffCont >> 64n) & 0x3FFFFFFFFFFFn;
|
|
348
|
+
} else {
|
|
349
|
+
// Combination: 11 G2 G3 G4 where G2 = (MSD-8), G3 G4 = expTop
|
|
350
|
+
const combo = 0x18n | ((BigInt(msd - 8) & 0x1n) << 2n) | expTop;
|
|
351
|
+
high |= combo << 58n;
|
|
352
|
+
// Exponent continuation (12 bits at position 121-110)
|
|
353
|
+
high |= expLow << 46n;
|
|
354
|
+
// Coefficient continuation high 46 bits
|
|
355
|
+
high |= (coeffCont >> 64n) & 0x3FFFFFFFFFFFn;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const buf = Buffer.alloc(16);
|
|
359
|
+
bufferWriteBigInt(buf, high, 0, 8);
|
|
360
|
+
bufferWriteBigInt(buf, low, 8, 8);
|
|
361
|
+
return buf;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Decode IEEE 754 Decimal128 format (16 bytes) to a string
|
|
366
|
+
* @param {Buffer} buffer - 16-byte buffer containing the Decimal128 encoding
|
|
367
|
+
* @returns {string|number} - Decoded value as string or special value
|
|
368
|
+
*/
|
|
369
|
+
function decodeDecimal128(buffer) {
|
|
370
|
+
if (buffer.length !== 16) {
|
|
371
|
+
throw new Error('Decimal128 buffer must be 16 bytes');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const high = bufferToBigInt(buffer.slice(0, 8));
|
|
375
|
+
const low = bufferToBigInt(buffer.slice(8, 16));
|
|
376
|
+
|
|
377
|
+
// Extract sign
|
|
378
|
+
const sign = (high >> 63n) & 0x1n;
|
|
379
|
+
|
|
380
|
+
// Check for special values
|
|
381
|
+
const combo = (high >> 58n) & 0x1Fn;
|
|
382
|
+
|
|
383
|
+
if ((combo & 0x1En) === 0x1En) {
|
|
384
|
+
// Special value (NaN or Infinity)
|
|
385
|
+
if ((combo & 0x1n) === 0n) {
|
|
386
|
+
return sign ? -Infinity : Infinity;
|
|
387
|
+
} else {
|
|
388
|
+
return NaN;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Decode exponent and coefficient
|
|
393
|
+
let exponent, coefficient, msd;
|
|
394
|
+
|
|
395
|
+
if ((combo & 0x18n) !== 0x18n) {
|
|
396
|
+
// Combination: G0 G1 G2 G3 G4 where G0 G1 != 11
|
|
397
|
+
// MSD is G2 G3 G4 (bits 2-0 of combo)
|
|
398
|
+
msd = Number(combo & 0x7n);
|
|
399
|
+
// Exponent top 2 bits are G0 G1 (bits 4-3 of combo)
|
|
400
|
+
const expTop = (combo >> 3n) & 0x3n;
|
|
401
|
+
// Exponent continuation is bits 121-110 (bits 57-46 in high, 12 bits)
|
|
402
|
+
const expLow = (high >> 46n) & 0xFFFn;
|
|
403
|
+
exponent = (expTop << 12n) | expLow;
|
|
404
|
+
// Coefficient continuation is bits 109-0 (bits 45-0 in high + all 64 bits in low = 110 bits)
|
|
405
|
+
const coeffHigh = high & 0x3FFFFFFFFFFFn; // 46 bits
|
|
406
|
+
coefficient = (coeffHigh << 64n) | low; // Combine with low 64 bits
|
|
407
|
+
coefficient = BigInt(msd) * 1000000000000000000000000000000000n + coefficient;
|
|
408
|
+
} else {
|
|
409
|
+
// Combination: 11 G2 G3 G4 (MSD 8-9)
|
|
410
|
+
// MSD is 8 + G2 (bit 2 of combo)
|
|
411
|
+
msd = 8 + Number((combo >> 2n) & 0x1n);
|
|
412
|
+
// Exponent top 2 bits are G3 G4 (bits 1-0 of combo)
|
|
413
|
+
const expTop = combo & 0x3n;
|
|
414
|
+
// Exponent continuation is bits 121-110 (12 bits)
|
|
415
|
+
const expLow = (high >> 46n) & 0xFFFn;
|
|
416
|
+
exponent = (expTop << 12n) | expLow;
|
|
417
|
+
// Coefficient continuation is bits 109-0 (110 bits)
|
|
418
|
+
const coeffHigh = high & 0x3FFFFFFFFFFFn; // 46 bits
|
|
419
|
+
coefficient = (coeffHigh << 64n) | low;
|
|
420
|
+
coefficient = BigInt(msd) * 1000000000000000000000000000000000n + coefficient;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Remove bias
|
|
424
|
+
const unbias = Number(exponent) - DECIMAL128_BIAS;
|
|
425
|
+
|
|
426
|
+
// Build result string
|
|
427
|
+
const coeffStr = coefficient.toString();
|
|
428
|
+
const signStr = sign ? '-' : '';
|
|
429
|
+
|
|
430
|
+
// Special case: if coefficient is 0, just return "0"
|
|
431
|
+
if (coefficient === 0n) {
|
|
432
|
+
return signStr + '0';
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (unbias === 0) {
|
|
436
|
+
return signStr + coeffStr;
|
|
437
|
+
} else if (unbias > 0) {
|
|
438
|
+
return signStr + coeffStr + '0'.repeat(unbias);
|
|
439
|
+
} else {
|
|
440
|
+
const absExp = -unbias;
|
|
441
|
+
if (absExp >= coeffStr.length) {
|
|
442
|
+
return signStr + '0.' + '0'.repeat(absExp - coeffStr.length) + coeffStr;
|
|
443
|
+
} else {
|
|
444
|
+
const intPart = coeffStr.substring(0, coeffStr.length - absExp);
|
|
445
|
+
const fracPart = coeffStr.substring(coeffStr.length - absExp);
|
|
446
|
+
return signStr + intPart + '.' + fracPart;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Convert a BigInt to a Buffer (big-endian)
|
|
453
|
+
* @param {BigInt} value - The BigInt value
|
|
454
|
+
* @param {number} size - Buffer size in bytes
|
|
455
|
+
* @returns {Buffer}
|
|
456
|
+
*/
|
|
457
|
+
function bigIntToBuffer(value, size) {
|
|
458
|
+
const buf = Buffer.alloc(size);
|
|
459
|
+
let val = BigInt(value.toString());
|
|
460
|
+
for (let i = size - 1; i >= 0; i--) {
|
|
461
|
+
buf[i] = Number(val & 0xFFn);
|
|
462
|
+
val >>= 8n;
|
|
463
|
+
}
|
|
464
|
+
return buf;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Convert a Buffer to BigInt (big-endian)
|
|
469
|
+
* @param {Buffer} buffer
|
|
470
|
+
* @returns {BigInt}
|
|
471
|
+
*/
|
|
472
|
+
function bufferToBigInt(buffer) {
|
|
473
|
+
let result = 0n;
|
|
474
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
475
|
+
result = (result << 8n) | BigInt(buffer[i]);
|
|
476
|
+
}
|
|
477
|
+
return result;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Write BigInt to buffer at offset (big-endian)
|
|
482
|
+
* @param {Buffer} buffer
|
|
483
|
+
* @param {BigInt} value
|
|
484
|
+
* @param {number} offset
|
|
485
|
+
* @param {number} size
|
|
486
|
+
*/
|
|
487
|
+
function bufferWriteBigInt(buffer, value, offset, size) {
|
|
488
|
+
let val = BigInt(value.toString());
|
|
489
|
+
for (let i = offset + size - 1; i >= offset; i--) {
|
|
490
|
+
buffer[i] = Number(val & 0xFFn);
|
|
491
|
+
val >>= 8n;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
module.exports = {
|
|
496
|
+
encodeDecimal64,
|
|
497
|
+
decodeDecimal64,
|
|
498
|
+
encodeDecimal128,
|
|
499
|
+
decodeDecimal128
|
|
500
|
+
};
|
package/lib/index.d.ts
CHANGED
|
@@ -41,21 +41,25 @@ declare module 'node-firebird' {
|
|
|
41
41
|
waitTimeout?: number;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
export type QueryOptions = {
|
|
45
|
+
timeout: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
44
48
|
export interface Database {
|
|
45
49
|
detach(callback?: SimpleCallback): Database;
|
|
46
50
|
transaction(options: TransactionOptions|Isolation|TransactionCallback, callback?: TransactionCallback): Database;
|
|
47
|
-
query(query: string, params: any[], callback: QueryCallback): Database;
|
|
48
|
-
execute(query: string, params: any[], callback: QueryCallback): Database;
|
|
49
|
-
sequentially(query: string, params: any[], rowCallback: SequentialCallback, callback: SimpleCallback,
|
|
51
|
+
query(query: string, params: any[], callback: QueryCallback, options?: QueryOptions): Database;
|
|
52
|
+
execute(query: string, params: any[], callback: QueryCallback, options?: QueryOptions): Database;
|
|
53
|
+
sequentially(query: string, params: any[], rowCallback: SequentialCallback, callback: SimpleCallback, options?: QueryOptions | boolean): Database;
|
|
50
54
|
drop(callback: SimpleCallback): void;
|
|
51
55
|
escape(value: any): string;
|
|
52
56
|
attachEvent(callback: any): this;
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
export interface Transaction {
|
|
56
|
-
query(query: string, params: any[], callback: QueryCallback): void;
|
|
57
|
-
execute(query: string, params: any[], callback: QueryCallback): void;
|
|
58
|
-
sequentially(query: string, params: any[], rowCallback: SequentialCallback, callback: SimpleCallback,
|
|
60
|
+
query(query: string, params: any[], callback: QueryCallback, options?: QueryOptions): void;
|
|
61
|
+
execute(query: string, params: any[], callback: QueryCallback, options?: QueryOptions): void;
|
|
62
|
+
sequentially(query: string, params: any[], rowCallback: SequentialCallback, callback: SimpleCallback, options?: QueryOptions | boolean): Database;
|
|
59
63
|
commit(callback?: SimpleCallback): void;
|
|
60
64
|
commitRetaining(callback?: SimpleCallback): void;
|
|
61
65
|
rollback(callback?: SimpleCallback): void;
|