Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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

Open
wants to merge 8 commits into
base: QueryParameters-FromNapiValue
Choose a base branch
Loading
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions 25 docs/src/internal/parameter_wrapper.md
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).
125 changes: 125 additions & 0 deletions 125 lib/new-utils.js
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");

Expand All @@ -23,6 +24,22 @@ const errorTypeMap = {

const concatenationMark = "#";

// BigInt constants
const isBigIntSupported = typeof BigInt !== "undefined";
Copy link
Collaborator

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
image

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
Expand Down Expand Up @@ -104,9 +121,117 @@ function arbitraryValueToBigInt(value) {
);
}

/**
* Converts a value to a varint buffer.
* @param {Integer|Buffer|String|Number} value
* @returns {Buffer}
* @private
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://jsdoc.app/tags-package
I think this tag should be used here

*/
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
Copy link
Preview

Copilot AI May 16, 2025

Choose a reason for hiding this comment

The 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
}
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) {
} else if (typeof value === "string") {
value = Integer.fromString(value);
}
let buf = null;
if (typeof value === "bigint") {
buf = encodeVarintFromBigInt(value);
} else if (value instanceof Buffer) {
buf = value;
} else if (value instanceof Integer) {

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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Collaborator

Choose a reason for hiding this comment

The 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) {
Copy link
Preview

Copilot AI May 16, 2025

Choose a reason for hiding this comment

The 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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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;
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.