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,
|
bindgen_runtime::Null, check_status, sys, type_of, Error, JsObject, Result, Status, ValueType,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "napi6")]
|
||||||
|
use super::BigInt;
|
||||||
use super::{FromNapiValue, Object, ToNapiValue};
|
use super::{FromNapiValue, Object, ToNapiValue};
|
||||||
|
|
||||||
impl ToNapiValue for Value {
|
impl ToNapiValue for Value {
|
||||||
|
@ -111,14 +113,30 @@ impl FromNapiValue for Map<String, Value> {
|
||||||
|
|
||||||
impl ToNapiValue for Number {
|
impl ToNapiValue for Number {
|
||||||
unsafe fn to_napi_value(env: sys::napi_env, n: Self) -> Result<sys::napi_value> {
|
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() {
|
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() {
|
} else if n.is_f64() {
|
||||||
unsafe { f64::to_napi_value(env, n.as_f64().unwrap()) }
|
unsafe { f64::to_napi_value(env, n.as_f64().unwrap()) }
|
||||||
} else {
|
} else {
|
||||||
let n = n.as_u64().unwrap();
|
let n = n.as_u64().unwrap();
|
||||||
if n > u32::MAX as u64 {
|
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 {
|
} else {
|
||||||
unsafe { u32::to_napi_value(env, n as u32) }
|
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 readPackageJson(): PackageJson␊
|
||||||
export function getPackageJsonName(packageJson: PackageJson): string␊
|
export function getPackageJsonName(packageJson: PackageJson): string␊
|
||||||
export function testSerdeRoundtrip(data: any): any␊
|
export function testSerdeRoundtrip(data: any): any␊
|
||||||
|
export function testSerdeBigNumberPrecision(number: string): any␊
|
||||||
export function returnFromSharedCrate(): Shared␊
|
export function returnFromSharedCrate(): Shared␊
|
||||||
export function contains(source: string, target: string): boolean␊
|
export function contains(source: string, target: string): boolean␊
|
||||||
export function concatStr(s: string): string␊
|
export function concatStr(s: string): string␊
|
||||||
|
|
Binary file not shown.
|
@ -90,6 +90,7 @@ import {
|
||||||
getStrFromObject,
|
getStrFromObject,
|
||||||
returnJsFunction,
|
returnJsFunction,
|
||||||
testSerdeRoundtrip,
|
testSerdeRoundtrip,
|
||||||
|
testSerdeBigNumberPrecision,
|
||||||
createObjWithProperty,
|
createObjWithProperty,
|
||||||
receiveObjectOnlyFromJs,
|
receiveObjectOnlyFromJs,
|
||||||
dateToNumber,
|
dateToNumber,
|
||||||
|
@ -495,6 +496,23 @@ test('serde-roundtrip', (t) => {
|
||||||
t.is(err!.message, 'JS symbols cannot be represented as a serde_json::Value')
|
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) => {
|
test('buffer', (t) => {
|
||||||
let buf = getBuffer()
|
let buf = getBuffer()
|
||||||
t.is(buf.toString('utf-8'), 'Hello world')
|
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 readPackageJson(): PackageJson
|
||||||
export function getPackageJsonName(packageJson: PackageJson): string
|
export function getPackageJsonName(packageJson: PackageJson): string
|
||||||
export function testSerdeRoundtrip(data: any): any
|
export function testSerdeRoundtrip(data: any): any
|
||||||
|
export function testSerdeBigNumberPrecision(number: string): any
|
||||||
export function returnFromSharedCrate(): Shared
|
export function returnFromSharedCrate(): Shared
|
||||||
export function contains(source: string, target: string): boolean
|
export function contains(source: string, target: string): boolean
|
||||||
export function concatStr(s: string): string
|
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 {
|
fn test_serde_roundtrip(data: Value) -> Value {
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
fn test_serde_big_number_precision(number: String) -> Value {
|
||||||
|
let data = format!("{{\"number\":{}}}", number);
|
||||||
|
serde_json::from_str(&data).unwrap()
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue