-
-
Notifications
You must be signed in to change notification settings - Fork 32
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
Support BigInt for i64 and u64 #24
Changes from 1 commit
37b3805
8baa1fa
ab0c892
4087cd8
69af2da
ea7ddeb
90dcf81
05a8010
6b6e901
c43908a
642a472
053845d
bfbfa3e
23d1dd7
067e8d0
cec3fb5
44153b7
d171675
f416074
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,17 @@ | ||
use js_sys::BigInt; | ||
use wasm_bindgen::prelude::*; | ||
|
||
#[wasm_bindgen(module = "/src/big-int-checks.js")] | ||
#[wasm_bindgen] | ||
extern "C" { | ||
pub fn to_u64(x: &BigInt) -> Option<u64>; | ||
#[wasm_bindgen(js_name = BigInt)] | ||
pub fn big_int_to_u64(x: &BigInt) -> u64; | ||
|
||
pub fn to_i64(x: &BigInt) -> Option<i64>; | ||
} | ||
#[wasm_bindgen(js_name = BigInt)] | ||
pub fn big_int_to_i64(x: &BigInt) -> i64; | ||
|
||
#[wasm_bindgen] | ||
extern "C" { | ||
#[wasm_bindgen(js_name = BigInt)] | ||
pub fn from_u64(x: u64) -> BigInt; | ||
pub fn big_int_from_u64(x: u64) -> BigInt; | ||
|
||
#[wasm_bindgen(js_name = BigInt)] | ||
pub fn from_i64(x: i64) -> BigInt; | ||
pub fn big_int_from_i64(x: i64) -> BigInt; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -215,7 +215,7 @@ impl ser::SerializeStruct for ObjectSerializer<'_> { | |
#[derive(Default)] | ||
pub struct Serializer { | ||
serialize_maps_as_objects: bool, | ||
serialize_64_bit_numbers_as_big_int: bool, | ||
serialize_large_number_types_as_big_ints: bool, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also nitpicking, but JS uses |
||
} | ||
|
||
impl Serializer { | ||
|
@@ -233,8 +233,8 @@ impl Serializer { | |
|
||
/// Set to `true` to serialize 64 bit numbers to JavaScript `BigInt` instead of | ||
/// `Number`. `false` by default | ||
pub fn serialize_64_bit_numbers_as_big_int(mut self, value: bool) -> Self { | ||
self.serialize_64_bit_numbers_as_big_int = value; | ||
pub fn serialize_large_number_types_as_big_ints(mut self, value: bool) -> Self { | ||
self.serialize_large_number_types_as_big_ints = value; | ||
self | ||
} | ||
} | ||
|
@@ -277,8 +277,8 @@ impl<'s> ser::Serializer for &'s Serializer { | |
} | ||
|
||
fn serialize_i64(self, v: i64) -> Result { | ||
if self.serialize_64_bit_numbers_as_big_int { | ||
return Ok(bindings::from_i64(v).into()); | ||
if self.serialize_large_number_types_as_big_ints { | ||
return Ok(bindings::big_int_from_i64(v).into()); | ||
} | ||
const MAX_SAFE_INTEGER: i64 = 9_007_199_254_740_991; | ||
const MIN_SAFE_INTEGER: i64 = -MAX_SAFE_INTEGER; | ||
|
@@ -292,9 +292,10 @@ impl<'s> ser::Serializer for &'s Serializer { | |
))) | ||
} | ||
} | ||
|
||
fn serialize_u64(self, v: u64) -> Result { | ||
if self.serialize_64_bit_numbers_as_big_int { | ||
return Ok(bindings::from_u64(v).into()); | ||
if self.serialize_large_number_types_as_big_ints { | ||
return Ok(bindings::big_int_from_u64(v).into()); | ||
} | ||
|
||
const MAX_SAFE_INTEGER: u64 = 9_007_199_254_740_991; | ||
|
@@ -309,6 +310,22 @@ impl<'s> ser::Serializer for &'s Serializer { | |
} | ||
} | ||
|
||
fn serialize_i128(self, v: i128) -> Result { | ||
if self.serialize_large_number_types_as_big_ints { | ||
Ok(JsValue::from(v)) | ||
} else { | ||
Err(Error::custom("i128 is not supported")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, shouldn't this fallback to JS number when option isn't activated? I'm of two minds on this, perhaps it's fine for it to error out, but then it would be good to at least expand the error message to clarify that it's supported, just needs to be enabled. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. The current behavior is to error so I was going to uphold that. In addition, I don't think there are many cases where it would be a good idea to convert a 128-bit integer to a 53 bit one (Js Number). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah erroring is fine if that's current behaviour, just saying the error message probably needs to be clearer and nudge the user in the right direction. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even something like "i128 is supported only in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My bad fixed the error in one place and not the other. It is updated now |
||
} | ||
} | ||
|
||
fn serialize_u128(self, v: u128) -> Result { | ||
if self.serialize_large_number_types_as_big_ints { | ||
Ok(JsValue::from(v)) | ||
} else { | ||
Err(Error::custom("u128 is not supported")) | ||
} | ||
} | ||
|
||
fn serialize_char(self, v: char) -> Result { | ||
Ok(JsString::from(v).into()) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
use js_sys::BigInt; | ||
use maplit::{btreemap, hashmap, hashset}; | ||
use serde::de::DeserializeOwned; | ||
use serde::ser::Error as SerError; | ||
|
@@ -180,11 +181,33 @@ fn numbers() { | |
to_value(&std::u64::MAX).unwrap_err(); | ||
} | ||
|
||
// Test u64 and i64 bigint serialization feature | ||
let big_int_serializer = Serializer::new().serialize_64_bit_numbers_as_big_int(true); | ||
// By default serializing i128 and u128 results in an error | ||
{ | ||
to_value(&0_i128).unwrap_err(); | ||
to_value(&0_u128).unwrap_err(); | ||
} | ||
|
||
// By default deserializing i128 and u128 uses 64 bit implementation | ||
{ | ||
assert_eq!(from_value::<i128>(JsValue::from(0_i128)).unwrap(), 0); | ||
assert_eq!(from_value::<i128>(JsValue::from(42_i128)).unwrap(), 42); | ||
assert_eq!(from_value::<i128>(JsValue::from(-42_i128)).unwrap(), -42); | ||
assert_eq!(from_value::<u128>(JsValue::from(0_u128)).unwrap(), 0); | ||
assert_eq!(from_value::<u128>(JsValue::from(42_u128)).unwrap(), 42); | ||
} | ||
|
||
// Test large number bigint serialization feature | ||
let big_int_serializer = Serializer::new().serialize_large_number_types_as_big_ints(true); | ||
{ | ||
const MAX_SAFE_INTEGER: i64 = 9_007_199_254_740_991; | ||
|
||
// Should be bigint | ||
0_i64 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: instead of converting, you can probably just do |
||
.serialize(&big_int_serializer) | ||
.unwrap() | ||
.dyn_into::<BigInt>() | ||
.unwrap(); | ||
|
||
// u64 and i64 should serialize the same | ||
test_via_into_with_config(0_i64, 0_u64, &big_int_serializer); | ||
test_via_into_with_config(42_i64, 42_u64, &big_int_serializer); | ||
|
@@ -232,6 +255,13 @@ fn numbers() { | |
{ | ||
const MAX_SAFE_INTEGER: u64 = 9_007_199_254_740_991; | ||
|
||
// Should be bigint | ||
0_u64 | ||
.serialize(&big_int_serializer) | ||
.unwrap() | ||
.dyn_into::<BigInt>() | ||
.unwrap(); | ||
|
||
// u64 and i64 should serialize the same | ||
test_via_into_with_config(0_u64, 0_i64, &big_int_serializer); | ||
test_via_into_with_config(42_u64, 42_i64, &big_int_serializer); | ||
|
@@ -267,6 +297,47 @@ fn numbers() { | |
JsValue::from(std::u64::MAX) | ||
); | ||
} | ||
|
||
// i128 and u128 should serialize to bigint when the feature is enabled | ||
{ | ||
// Should be bigint | ||
0_i128 | ||
.serialize(&big_int_serializer) | ||
.unwrap() | ||
.dyn_into::<BigInt>() | ||
.unwrap(); | ||
|
||
// i128 and u128 should serialize the same | ||
test_via_into_with_config(0_u128, 0_i128, &big_int_serializer); | ||
test_via_into_with_config(42_u128, 42_i128, &big_int_serializer); | ||
|
||
// Can still deserialize from JS numbers | ||
assert_eq!(from_value::<i128>(JsValue::from_f64(1.0)).unwrap(), 1); | ||
|
||
// Invalid floats should fail | ||
from_value::<i128>(JsValue::from_f64(1.5)).unwrap_err(); | ||
from_value::<i128>(JsValue::from_f64(-10.2)).unwrap_err(); | ||
} | ||
|
||
{ | ||
// Should be bigint | ||
0_u128 | ||
.serialize(&big_int_serializer) | ||
.unwrap() | ||
.dyn_into::<BigInt>() | ||
.unwrap(); | ||
|
||
// i128 and u128 should serialize the same | ||
test_via_into_with_config(0_i128, 0_u128, &big_int_serializer); | ||
test_via_into_with_config(42_i128, 42_u128, &big_int_serializer); | ||
|
||
// Can still deserialize from JS numbers | ||
assert_eq!(from_value::<u128>(JsValue::from_f64(1.0)).unwrap(), 1); | ||
|
||
// Invalid floats should fail | ||
from_value::<u128>(JsValue::from_f64(1.5)).unwrap_err(); | ||
from_value::<u128>(JsValue::from_f64(-10.2)).unwrap_err(); | ||
} | ||
} | ||
|
||
#[wasm_bindgen_test] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: it's
BigInt
(same in the other message below).