Skip to content

Commit 6669860

Browse files
committed
fix: enhance serialization for i64 and u64 types in Serializer
1 parent 4c53a36 commit 6669860

File tree

2 files changed

+59
-12
lines changed

2 files changed

+59
-12
lines changed

src/serde/ser.rs

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ use libquickjs_ng_sys::{JSContext, JSValue};
22
use serde::{ser, Serialize};
33

44
use crate::utils::{
5-
create_bool, create_empty_array, create_empty_object, create_float, create_int, create_null,
6-
create_string, create_undefined, own_raw_value,
5+
create_bigint, create_bool, create_empty_array, create_empty_object, create_float, create_int,
6+
create_null, create_string, create_undefined, own_raw_value,
77
};
88
use crate::value::{OwnedJsArray, OwnedJsObject, OwnedJsValue};
99

1010
use super::error::{Error, Result};
1111

12+
const MAX_SAFE_INTEGER: u64 = 9007199254740991;
13+
1214
/// A structure that serializes Rust values into JS values.
1315
pub struct Serializer {
1416
context: *mut JSContext,
@@ -153,39 +155,65 @@ impl<'a> ser::Serializer for &'a mut Serializer {
153155
// will be serialized the same. Other formats, especially compact binary
154156
// formats, may need independent logic for the different sizes.
155157
fn serialize_i8(self, v: i8) -> Result<()> {
156-
self.serialize_i64(i64::from(v))
158+
self.serialize_i32(i32::from(v))
157159
}
158160

159161
fn serialize_i16(self, v: i16) -> Result<()> {
160-
self.serialize_i64(i64::from(v))
162+
self.serialize_i32(i32::from(v))
161163
}
162164

163165
fn serialize_i32(self, v: i32) -> Result<()> {
164-
self.serialize_i64(i64::from(v))
166+
let value = create_int(self.context, v as i32);
167+
self.set_node_value(value)
165168
}
166169

167170
// Not particularly efficient but this is example code anyway. A more
168171
// performant approach would be to use the `itoa` crate.
169172
fn serialize_i64(self, v: i64) -> Result<()> {
170-
let value = create_int(self.context, v as i32);
171-
self.set_node_value(value)
173+
if v > MAX_SAFE_INTEGER as i64 {
174+
let value = create_bigint(self.context, (v as i64).into())?;
175+
self.set_node_value(value)
176+
} else if v > i32::MAX as i64 {
177+
let value = create_float(self.context, v as f64);
178+
self.set_node_value(value)
179+
} else {
180+
let value = create_int(self.context, v as i32);
181+
self.set_node_value(value)
182+
}
172183
}
173184

174185
fn serialize_u8(self, v: u8) -> Result<()> {
175-
self.serialize_u64(u64::from(v))
186+
self.serialize_i32(i32::from(v))
176187
}
177188

178189
fn serialize_u16(self, v: u16) -> Result<()> {
179-
self.serialize_u64(u64::from(v))
190+
self.serialize_i32(i32::from(v))
180191
}
181192

182193
fn serialize_u32(self, v: u32) -> Result<()> {
183-
self.serialize_u64(u64::from(v))
194+
if v > i32::MAX as u32 {
195+
let value = create_float(self.context, v as f64);
196+
self.set_node_value(value)
197+
} else {
198+
let value = create_int(self.context, v as i32);
199+
self.set_node_value(value)
200+
}
184201
}
185202

186203
fn serialize_u64(self, v: u64) -> Result<()> {
187-
let value = create_int(self.context, v as i32);
188-
self.set_node_value(value)
204+
if v > i64::MAX as u64 {
205+
let value = create_bigint(self.context, num_bigint::BigInt::from(v).into())?;
206+
self.set_node_value(value)
207+
} else if v > MAX_SAFE_INTEGER {
208+
let value = create_bigint(self.context, (v as i64).into())?;
209+
self.set_node_value(value)
210+
} else if v > i32::MAX as u64 {
211+
let value = create_float(self.context, v as f64);
212+
self.set_node_value(value)
213+
} else {
214+
let value = create_int(self.context, v as i32);
215+
self.set_node_value(value)
216+
}
189217
}
190218

191219
fn serialize_f32(self, v: f32) -> Result<()> {

tests/serde.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ fn serde_ser_int() {
2626
assert_eq!(js_value.to_json_string(0).unwrap(), "123");
2727
}
2828

29+
#[test]
30+
fn serde_ser_int64() {
31+
let context = Context::builder().build().unwrap();
32+
// int
33+
// TODO: should take care of i32, i64, u32, u64, etc.
34+
let value: u64 = 1754784747637;
35+
let js_value = to_js(unsafe { context.context_raw() }, &value).unwrap();
36+
37+
assert_eq!(js_value.to_json_string(0).unwrap(), "1754784747637");
38+
}
39+
2940
#[test]
3041
fn serde_ser_float() {
3142
let context = Context::builder().build().unwrap();
@@ -257,6 +268,14 @@ fn serde_de_signed_interger() {
257268
assert_eq!(parse_from_js::<i32>(value), -1234);
258269
}
259270

271+
#[test]
272+
fn serde_de_i64() {
273+
let value = json!(1754784747637 as i64);
274+
275+
// number larger than i32::MAX is treated as f64 in quickjs
276+
assert_eq!(parse_from_js::<f64>(value), 1754784747637.0);
277+
}
278+
260279
#[test]
261280
fn serde_de_float() {
262281
let value = json!(3.14159265);

0 commit comments

Comments
 (0)