Repository: napi-rs/napi-rs
File: crates/napi/src/bindgen_runtime/js_values/bigint.rs (Rust)

Code before:

use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue};

/// i64 is converted to `Number`
#[repr(transparent)]
#[allow(non_camel_case_types)]
pub struct i64n(pub i64);

/// <https://nodejs.org/api/n-api.html#napi_create_bigint_words>
/// The resulting BigInt is calculated as: (–1)^sign_bit (words\[0\] × (2^64)^0 + words\[1\] × (2^64)^1 + …)
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BigInt {
  /// true for negative numbers
  pub sign_bit: bool,
  pub words: Vec<u64>,
}

impl TypeName for BigInt {
  fn type_name() -> &'static str {
    "BigInt"
  }

  fn value_type() -> crate::ValueType {
    crate::ValueType::BigInt
  }
}

impl ValidateNapiValue for BigInt {}

impl FromNapiValue for BigInt {
  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
    let mut word_count = 0usize;
    check_status!(unsafe {
      sys::napi_get_value_bigint_words(
        env,
        napi_val,
        ptr::null_mut(),
        &mut word_count,
        ptr::null_mut(),
      )
    })?;
    let mut words: Vec<u64> = Vec::with_capacity(word_count);
    let mut sign_bit = 0;

    unsafe {
      check_status!(sys::napi_get_value_bigint_words(
        env,
        napi_val,
        &mut sign_bit,
        &mut word_count,
        words.as_mut_ptr(),

Review comment (suggestion):
The sign bit comparison should occur before checking the words, or should handle the special case where all words are zero. When all words are zero (representing the value 0), the sign bit should be ignored, as +0 and -0 should be considered equal. Currently, if JavaScript BigInt were to represent -0n with `sign_bit = true` and `words = [0]`, it would not equal +0n with `sign_bit = false` and `words = [0]`, even though they represent the same mathematical value.

Suggested fix:
```rust
impl PartialEq for BigInt {
  fn eq(&self, other: &Self) -> bool {
    // Check if both represent zero
    let self_is_zero = self.words.iter().all(|&w| w == 0);
    let other_is_zero = other.words.iter().all(|&w| w == 0);
    
    if self_is_zero && other_is_zero {
      return true; // Both are zero, ignore sign bit
    }
    
    // For non-zero values, sign bits must match
    if self.sign_bit != other.sign_bit {
      return false;
    }
    
    // Compare words with trailing zeros
    for i in 0..max(self.words.len(), other.words.len()) {
      if self.words.get(i).unwrap_or(&0) != other.words.get(i).unwrap_or(&0) {
        return false;
      }
    }
    true
  }
}
```

Code after:

use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue};

/// i64 is converted to `Number`
#[repr(transparent)]
#[allow(non_camel_case_types)]
pub struct i64n(pub i64);

/// <https://nodejs.org/api/n-api.html#napi_create_bigint_words>
/// The resulting BigInt is calculated as: (–1)^sign_bit (words\[0\] × (2^64)^0 + words\[1\] × (2^64)^1 + …)
#[derive(Debug, Clone, Eq)]
pub struct BigInt {
  /// true for negative numbers
  pub sign_bit: bool,
  pub words: Vec<u64>,
}

impl PartialEq for BigInt {
  fn eq(&self, other: &Self) -> bool {
    for i in 0..max(self.words.len(), other.words.len()) {
      if self.words.get(i).unwrap_or(&0) != other.words.get(i).unwrap_or(&0) {
        return false;
      }
    }
    self.sign_bit == other.sign_bit
  }
}

impl TypeName for BigInt {
  fn type_name() -> &'static str {
    "BigInt"
  }

  fn value_type() -> crate::ValueType {
    crate::ValueType::BigInt
  }
}

impl ValidateNapiValue for BigInt {}

impl FromNapiValue for BigInt {
  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
    let mut word_count = 0usize;
    check_status!(unsafe {
      sys::napi_get_value_bigint_words(
        env,
        napi_val,
        ptr::null_mut(),
        &mut word_count,
        ptr::null_mut(),
      )