-
Notifications
You must be signed in to change notification settings - Fork 1
Varint #195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: QueryParameters-FromNapiValue
Are you sure you want to change the base?
Varint #195
Changes from all commits
48d8ec8
715d5a1
1dfdd1c
5b6ee50
be1a8bc
2d7651b
e1f22a5
dc7350c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# ParameterWrapper | ||
|
||
Parameter wrapper is used to pass parameters for all queries. | ||
It can pass any CQL Value, null and unset values. | ||
On the Rust side, the value is represented by: | ||
`Option<MaybeUnset<CqlValue>>` | ||
and on the JavaScript side it's represented by: | ||
`[ComplexType, Value]` with the null being: `[]` and unset: `[undefined]`. | ||
|
||
The conversion from the user provided values to accepted format is done in `types/cql-utils.js`. | ||
|
||
On the Rust side, `requests/parameter_wrappers.rs` is responsible for value conversion | ||
into format recognized by the Rust driver. It's done via the `FromNapiValue` trait. | ||
The specific format containing both type and value is necessary to create a correct CQL Value, | ||
without using [env](https://napi.rs/docs/compat-mode/concepts/env) in function. | ||
|
||
As driver allows values to be provided in multiple formats: | ||
|
||
- one of the predefined types, | ||
- as a string representation of the type, | ||
- as a pre-serialized byte array | ||
|
||
which are converted into predefined type, before passing it to the Rust. | ||
It's possible to do this conversion also on the Rust size | ||
(but it's necessary to check the performance impact of such change). |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,6 +1,7 @@ | ||||||||||||||||||||||||||||||||||||||||||
"use strict"; | ||||||||||||||||||||||||||||||||||||||||||
const customErrors = require("./errors"); | ||||||||||||||||||||||||||||||||||||||||||
const util = require("./utils"); | ||||||||||||||||||||||||||||||||||||||||||
const Integer = require("./types/integer"); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
const Long = require("long"); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
|
@@ -23,6 +24,22 @@ const errorTypeMap = { | |||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
const concatenationMark = "#"; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
// BigInt constants | ||||||||||||||||||||||||||||||||||||||||||
const isBigIntSupported = typeof BigInt !== "undefined"; | ||||||||||||||||||||||||||||||||||||||||||
const bigInt8 = isBigIntSupported ? BigInt(8) : null; | ||||||||||||||||||||||||||||||||||||||||||
const bigInt0 = isBigIntSupported ? BigInt(0) : null; | ||||||||||||||||||||||||||||||||||||||||||
const bigIntMinus1 = isBigIntSupported ? BigInt(-1) : null; | ||||||||||||||||||||||||||||||||||||||||||
const bigInt8BitsOn = isBigIntSupported ? BigInt(0xff) : null; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
// Buffer constants | ||||||||||||||||||||||||||||||||||||||||||
const buffers = { | ||||||||||||||||||||||||||||||||||||||||||
int16Zero: util.allocBufferFromArray([0, 0]), | ||||||||||||||||||||||||||||||||||||||||||
int32Zero: util.allocBufferFromArray([0, 0, 0, 0]), | ||||||||||||||||||||||||||||||||||||||||||
int8Zero: util.allocBufferFromArray([0]), | ||||||||||||||||||||||||||||||||||||||||||
int8One: util.allocBufferFromArray([1]), | ||||||||||||||||||||||||||||||||||||||||||
int8MaxValue: util.allocBufferFromArray([0xff]), | ||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* A wrapper function to map napi errors to Node.js errors or custom errors. | ||||||||||||||||||||||||||||||||||||||||||
* Because NAPI-RS does not support throwing errors different that Error, for example | ||||||||||||||||||||||||||||||||||||||||||
|
@@ -104,9 +121,117 @@ function arbitraryValueToBigInt(value) { | |||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Converts a value to a varint buffer. | ||||||||||||||||||||||||||||||||||||||||||
* @param {Integer|Buffer|String|Number} value | ||||||||||||||||||||||||||||||||||||||||||
* @returns {Buffer} | ||||||||||||||||||||||||||||||||||||||||||
* @private | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://jsdoc.app/tags-package |
||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
function encodeVarint(value) { | ||||||||||||||||||||||||||||||||||||||||||
if (typeof value === "number") { | ||||||||||||||||||||||||||||||||||||||||||
value = Integer.fromNumber(value); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
if (typeof value === "string") { | ||||||||||||||||||||||||||||||||||||||||||
value = Integer.fromString(value); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
let buf = null; | ||||||||||||||||||||||||||||||||||||||||||
if (typeof value === "bigint") { | ||||||||||||||||||||||||||||||||||||||||||
buf = encodeVarintFromBigInt(value); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
if (value instanceof Buffer) { | ||||||||||||||||||||||||||||||||||||||||||
buf = value; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
if (value instanceof Integer) { | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+133
to
+144
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nitpick] Consider refactoring the series of type checks in encodeVarint into an if-else chain to ensure that only one conversion branch is executed, which can improve readability and reduce potential conversion ambiguities.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||||||||||||||||||||
buf = Integer.toBuffer(value); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
if (buf === null) { | ||||||||||||||||||||||||||||||||||||||||||
throw new TypeError( | ||||||||||||||||||||||||||||||||||||||||||
"Not a valid varint, expected Integer/Number/String/Buffer, obtained " + | ||||||||||||||||||||||||||||||||||||||||||
util.inspect(value), | ||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
return buf; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Converts a BigInt to a varint buffer. | ||||||||||||||||||||||||||||||||||||||||||
* @param {String|BigInt} value | ||||||||||||||||||||||||||||||||||||||||||
* @returns {Buffer} | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
function encodeVarintFromBigInt(value) { | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+156
to
+161
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to see a lot of unit tests for this function (unless you decide to switch the approach) |
||||||||||||||||||||||||||||||||||||||||||
if (typeof value === "string") { | ||||||||||||||||||||||||||||||||||||||||||
// All numeric types are supported as strings for historical reasons | ||||||||||||||||||||||||||||||||||||||||||
value = BigInt(value); | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+162
to
+164
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haven't we decided to drop support for providing numerical values as strings? @adespawn There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That was for non big int integers. Here, I believe it's a different case and we should consider it separately from the "normal" integers |
||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if (typeof value !== "bigint") { | ||||||||||||||||||||||||||||||||||||||||||
throw new TypeError( | ||||||||||||||||||||||||||||||||||||||||||
"Not a valid varint, expected BigInt, obtained " + | ||||||||||||||||||||||||||||||||||||||||||
util.inspect(value), | ||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if (value === bigInt0) { | ||||||||||||||||||||||||||||||||||||||||||
return buffers.int8Zero; | ||||||||||||||||||||||||||||||||||||||||||
} else if (value === bigIntMinus1) { | ||||||||||||||||||||||||||||||||||||||||||
return buffers.int8MaxValue; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
const parts = []; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if (value > bigInt0) { | ||||||||||||||||||||||||||||||||||||||||||
while (value !== bigInt0) { | ||||||||||||||||||||||||||||||||||||||||||
parts.unshift(Number(value & bigInt8BitsOn)); | ||||||||||||||||||||||||||||||||||||||||||
value = value >> bigInt8; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if (parts[0] > 0x7f) { | ||||||||||||||||||||||||||||||||||||||||||
// Positive value needs a padding | ||||||||||||||||||||||||||||||||||||||||||
parts.unshift(0); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||
while (value !== bigIntMinus1) { | ||||||||||||||||||||||||||||||||||||||||||
parts.unshift(Number(value & bigInt8BitsOn)); | ||||||||||||||||||||||||||||||||||||||||||
value = value >> bigInt8; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if (parts[0] <= 0x7f) { | ||||||||||||||||||||||||||||||||||||||||||
// Negative value needs a padding | ||||||||||||||||||||||||||||||||||||||||||
parts.unshift(0xff); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
return util.allocBufferFromArray(parts); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Converts a byte array to a BigInt | ||||||||||||||||||||||||||||||||||||||||||
* @param {Buffer} bytes | ||||||||||||||||||||||||||||||||||||||||||
* @returns {BigInt} | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
function decodeVarintAsBigInt(bytes) { | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nitpick] It would be helpful to add a brief comment or JSDoc note about the expected type and format of the 'bytes' parameter in decodeVarintAsBigInt to aid future maintainers in understanding its assumptions. Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback
Comment on lines
+207
to
+212
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Ditto) I want to see a lot of unit tests for this function (unless you decide to switch the approach) |
||||||||||||||||||||||||||||||||||||||||||
let result = bigInt0; | ||||||||||||||||||||||||||||||||||||||||||
if (bytes[0] <= 0x7f) { | ||||||||||||||||||||||||||||||||||||||||||
for (let i = 0; i < bytes.length; i++) { | ||||||||||||||||||||||||||||||||||||||||||
const b = BigInt(bytes[bytes.length - 1 - i]); | ||||||||||||||||||||||||||||||||||||||||||
result = result | (b << BigInt(i * 8)); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||
for (let i = 0; i < bytes.length; i++) { | ||||||||||||||||||||||||||||||||||||||||||
const b = BigInt(bytes[bytes.length - 1 - i]); | ||||||||||||||||||||||||||||||||||||||||||
result = result | ((~b & bigInt8BitsOn) << BigInt(i * 8)); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
result = ~result; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
return result; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
exports.throwNotSupported = throwNotSupported; | ||||||||||||||||||||||||||||||||||||||||||
exports.napiErrorHandler = napiErrorHandler; | ||||||||||||||||||||||||||||||||||||||||||
exports.throwNotSupported = throwNotSupported; | ||||||||||||||||||||||||||||||||||||||||||
exports.bigintToLong = bigintToLong; | ||||||||||||||||||||||||||||||||||||||||||
exports.longToBigint = longToBigint; | ||||||||||||||||||||||||||||||||||||||||||
exports.arbitraryValueToBigInt = arbitraryValueToBigInt; | ||||||||||||||||||||||||||||||||||||||||||
exports.encodeVarint = encodeVarint; | ||||||||||||||||||||||||||||||||||||||||||
exports.decodeVarintAsBigInt = decodeVarintAsBigInt; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BigInt was introduced in Node10. (see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#browser_compatibility). I don't think we want to support version of node that are end-of-life for the last 3-4 years
