feat(napi): serialize BigInt to serde::Value (#2659)

This commit is contained in:
LongYinan 2025-05-24 23:18:40 +08:00 committed by GitHub
parent abf041542c
commit f8fab45de7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 79 additions and 6 deletions

View File

@ -169,15 +169,19 @@ pub(crate) unsafe fn u128_with_sign_to_napi_value(
let mut raw_value = ptr::null_mut();
if cfg!(target_endian = "little") {
let words = &val as *const u128 as *const u64;
check_status!(unsafe {
sys::napi_create_bigint_words(env, sign_bit, 2, words, &mut raw_value)
})?;
check_status!(
unsafe { sys::napi_create_bigint_words(env, sign_bit, 2, words, &mut raw_value) },
"Failed to create BigInt from u128"
)?;
return Ok(raw_value);
}
let arr: [u64; 2] = [val as _, (val >> 64) as _];
let words = &arr as *const u64;
check_status!(unsafe { sys::napi_create_bigint_words(env, sign_bit, 2, words, &mut raw_value) })?;
check_status!(
unsafe { sys::napi_create_bigint_words(env, sign_bit, 2, words, &mut raw_value) },
"Failed to create BigInt from u128"
)?;
Ok(raw_value)
}
@ -242,7 +246,10 @@ impl ToNapiValue for &mut i64n {
impl ToNapiValue for u64 {
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
let mut raw_value = ptr::null_mut();
check_status!(unsafe { sys::napi_create_bigint_uint64(env, val, &mut raw_value) })?;
check_status!(
unsafe { sys::napi_create_bigint_uint64(env, val, &mut raw_value) },
"Failed to create BigInt from u64"
)?;
Ok(raw_value)
}
}

View File

@ -1,4 +1,5 @@
use std::marker::PhantomData;
use std::ptr;
use serde_json::{Map, Number, Value};
@ -51,7 +52,25 @@ impl FromNapiValue for Value {
}
}
#[cfg(feature = "napi6")]
ValueType::BigInt => todo!(),
ValueType::BigInt => {
let n = unsafe { BigInt::from_napi_value(env, napi_val)? };
// negative
if n.sign_bit {
let (v, lossless) = n.get_i64();
if lossless {
Value::Number(v.into())
} else {
Value::String(to_string(env, napi_val)?)
}
} else {
let (_, v, lossless) = n.get_u64();
if lossless {
Value::Number(v.into())
} else {
Value::String(to_string(env, napi_val)?)
}
}
}
ValueType::Null => Value::Null,
ValueType::Function => {
return Err(Error::new(
@ -89,6 +108,13 @@ impl FromNapiValue for Value {
}
}
fn to_string(env: sys::napi_env, napi_val: sys::napi_value) -> Result<String> {
let mut string = ptr::null_mut();
check_status!(unsafe { sys::napi_coerce_to_string(env, napi_val, &mut string) })?;
let s = unsafe { String::from_napi_value(env, string) }?;
Ok(s)
}
impl ToNapiValue for &Map<String, Value> {
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
let mut obj = Object::new(&Env::from(env))?;

View File

@ -572,6 +572,8 @@ Generated by [AVA](https://avajs.dev).
export declare function generateFunctionAndCallIt(): FunctionData␊
export declare function getBigintJsonValue(value: bigint): void␊
export declare function getBtreeMapping(): Record<string, number>
export declare function getBuffer(): Buffer␊

View File

@ -137,6 +137,7 @@ import {
testSerdeRoundtrip,
testSerdeBigNumberPrecision,
testSerdeBufferBytes,
getBigintJsonValue,
createObjWithProperty,
receiveObjectOnlyFromJs,
dateToNumber,
@ -883,6 +884,14 @@ test('serde-buffer-bytes', (t) => {
t.is(testSerdeBufferBytes({ code: new ArrayBuffer(0) }), 0n)
})
test('get bigint json value', (t) => {
t.notThrows(() => {
getBigintJsonValue(-1n)
getBigintJsonValue(1n)
getBigintJsonValue(18446744073709551620n)
})
})
test('buffer', (t) => {
let buf = getBuffer()
t.is(buf.toString('utf-8'), 'Hello world')

View File

@ -210,6 +210,7 @@ export const f64ArrayToArray = __napiModule.exports.f64ArrayToArray
export const fibonacci = __napiModule.exports.fibonacci
export const fnReceivedAliased = __napiModule.exports.fnReceivedAliased
export const generateFunctionAndCallIt = __napiModule.exports.generateFunctionAndCallIt
export const getBigintJsonValue = __napiModule.exports.getBigintJsonValue
export const getBtreeMapping = __napiModule.exports.getBtreeMapping
export const getBuffer = __napiModule.exports.getBuffer
export const getBufferSlice = __napiModule.exports.getBufferSlice

View File

@ -234,6 +234,7 @@ module.exports.f64ArrayToArray = __napiModule.exports.f64ArrayToArray
module.exports.fibonacci = __napiModule.exports.fibonacci
module.exports.fnReceivedAliased = __napiModule.exports.fnReceivedAliased
module.exports.generateFunctionAndCallIt = __napiModule.exports.generateFunctionAndCallIt
module.exports.getBigintJsonValue = __napiModule.exports.getBigintJsonValue
module.exports.getBtreeMapping = __napiModule.exports.getBtreeMapping
module.exports.getBuffer = __napiModule.exports.getBuffer
module.exports.getBufferSlice = __napiModule.exports.getBufferSlice

View File

@ -524,6 +524,7 @@ module.exports.f64ArrayToArray = nativeBinding.f64ArrayToArray
module.exports.fibonacci = nativeBinding.fibonacci
module.exports.fnReceivedAliased = nativeBinding.fnReceivedAliased
module.exports.generateFunctionAndCallIt = nativeBinding.generateFunctionAndCallIt
module.exports.getBigintJsonValue = nativeBinding.getBigintJsonValue
module.exports.getBtreeMapping = nativeBinding.getBtreeMapping
module.exports.getBuffer = nativeBinding.getBuffer
module.exports.getBufferSlice = nativeBinding.getBufferSlice

View File

@ -534,6 +534,8 @@ export interface FunctionData {
export declare function generateFunctionAndCallIt(): FunctionData
export declare function getBigintJsonValue(value: bigint): void
export declare function getBtreeMapping(): Record<string, number>
export declare function getBuffer(): Buffer

View File

@ -69,3 +69,27 @@ impl PackageJsonReader {
&self.i
}
}
#[napi(catch_unwind, ts_args_type = "value: bigint")]
pub fn get_bigint_json_value(bigint_json_value: Value) {
match bigint_json_value {
Value::Number(n) => {
if let Some(u) = n.as_u64() {
assert_eq!(u, 1);
return;
}
if let Some(i) = n.as_i64() {
assert_eq!(i, -1);
return;
}
unreachable!("should not happen");
}
Value::String(s) => {
assert_eq!(s, "18446744073709551620");
return;
}
_ => {
unreachable!("should not happen");
}
}
}