fix(napi): big numbers losing precision on serde_json::Value (#1538)
* fix(napi): big numbers losing precision on serde_json::Value * fix(napi): add feature flags for bigint on number conversion * chore(napi): change MAX_SAFE_INT to constant and convert big numbers to string when bigint is not available * chore(napi): change how the check for safe integer range is made
This commit is contained in:
parent
2e865cad29
commit
3983be23f5
6 changed files with 46 additions and 2 deletions
|
@ -4,6 +4,8 @@ use crate::{
|
|||
bindgen_runtime::Null, check_status, sys, type_of, Error, JsObject, Result, Status, ValueType,
|
||||
};
|
||||
|
||||
#[cfg(feature = "napi6")]
|
||||
use super::BigInt;
|
||||
use super::{FromNapiValue, Object, ToNapiValue};
|
||||
|
||||
impl ToNapiValue for Value {
|
||||
|
@ -111,14 +113,30 @@ impl FromNapiValue for Map<String, Value> {
|
|||
|
||||
impl ToNapiValue for Number {
|
||||
unsafe fn to_napi_value(env: sys::napi_env, n: Self) -> Result<sys::napi_value> {
|
||||
#[cfg(feature = "napi6")]
|
||||
const MAX_SAFE_INT: i64 = 9007199254740991i64; // 2 ^ 53 - 1
|
||||
if n.is_i64() {
|
||||
unsafe { i64::to_napi_value(env, n.as_i64().unwrap()) }
|
||||
let n = n.as_i64().unwrap();
|
||||
#[cfg(feature = "napi6")]
|
||||
{
|
||||
if !(-MAX_SAFE_INT..=MAX_SAFE_INT).contains(&n) {
|
||||
return unsafe { BigInt::to_napi_value(env, BigInt::from(n)) };
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { i64::to_napi_value(env, n) }
|
||||
} else if n.is_f64() {
|
||||
unsafe { f64::to_napi_value(env, n.as_f64().unwrap()) }
|
||||
} else {
|
||||
let n = n.as_u64().unwrap();
|
||||
if n > u32::MAX as u64 {
|
||||
todo!("impl BigInt")
|
||||
#[cfg(feature = "napi6")]
|
||||
{
|
||||
return unsafe { BigInt::to_napi_value(env, BigInt::from(n)) };
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "napi6"))]
|
||||
return unsafe { String::to_napi_value(env, n.to_string()) };
|
||||
} else {
|
||||
unsafe { u32::to_napi_value(env, n as u32) }
|
||||
}
|
||||
|
|
|
@ -195,6 +195,7 @@ Generated by [AVA](https://avajs.dev).
|
|||
export function readPackageJson(): PackageJson␊
|
||||
export function getPackageJsonName(packageJson: PackageJson): string␊
|
||||
export function testSerdeRoundtrip(data: any): any␊
|
||||
export function testSerdeBigNumberPrecision(number: string): any␊
|
||||
export function returnFromSharedCrate(): Shared␊
|
||||
export function contains(source: string, target: string): boolean␊
|
||||
export function concatStr(s: string): string␊
|
||||
|
|
Binary file not shown.
|
@ -90,6 +90,7 @@ import {
|
|||
getStrFromObject,
|
||||
returnJsFunction,
|
||||
testSerdeRoundtrip,
|
||||
testSerdeBigNumberPrecision,
|
||||
createObjWithProperty,
|
||||
receiveObjectOnlyFromJs,
|
||||
dateToNumber,
|
||||
|
@ -495,6 +496,23 @@ test('serde-roundtrip', (t) => {
|
|||
t.is(err!.message, 'JS symbols cannot be represented as a serde_json::Value')
|
||||
})
|
||||
|
||||
test('serde-large-number-precision', (t) => {
|
||||
t.is(testSerdeBigNumberPrecision('12345').number, 12345)
|
||||
t.is(
|
||||
testSerdeBigNumberPrecision('123456789012345678901234567890').number,
|
||||
1.2345678901234568e29,
|
||||
)
|
||||
t.is(
|
||||
testSerdeBigNumberPrecision('123456789012345678901234567890.123456789')
|
||||
.number,
|
||||
1.2345678901234568e29,
|
||||
)
|
||||
t.is(
|
||||
testSerdeBigNumberPrecision('109775245175819965').number.toString(),
|
||||
'109775245175819965',
|
||||
)
|
||||
})
|
||||
|
||||
test('buffer', (t) => {
|
||||
let buf = getBuffer()
|
||||
t.is(buf.toString('utf-8'), 'Hello world')
|
||||
|
|
1
examples/napi/index.d.ts
vendored
1
examples/napi/index.d.ts
vendored
|
@ -185,6 +185,7 @@ export interface PackageJson {
|
|||
export function readPackageJson(): PackageJson
|
||||
export function getPackageJsonName(packageJson: PackageJson): string
|
||||
export function testSerdeRoundtrip(data: any): any
|
||||
export function testSerdeBigNumberPrecision(number: string): any
|
||||
export function returnFromSharedCrate(): Shared
|
||||
export function contains(source: string, target: string): boolean
|
||||
export function concatStr(s: string): string
|
||||
|
|
|
@ -30,3 +30,9 @@ fn get_package_json_name(package_json: PackageJson) -> String {
|
|||
fn test_serde_roundtrip(data: Value) -> Value {
|
||||
data
|
||||
}
|
||||
|
||||
#[napi]
|
||||
fn test_serde_big_number_precision(number: String) -> Value {
|
||||
let data = format!("{{\"number\":{}}}", number);
|
||||
serde_json::from_str(&data).unwrap()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue