Skip to content

Commit 4ba2c7f

Browse files
committed
inline_int
1 parent 3be7253 commit 4ba2c7f

File tree

5 files changed

+62
-21
lines changed

5 files changed

+62
-21
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ yyjson = []
5454
intrinsics = []
5555
optimize = []
5656
strict_provenance = []
57+
inline_int = []
5758

5859
[dependencies]
5960
arrayvec = { version = "0.7", default-features = false, features = ["std", "serde"] }

build.rs

+3
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ fn main() {
5454
}
5555
}
5656

57+
#[cfg(target_pointer_width = "64")]
58+
println!("cargo:rustc-cfg=feature=\"inline_int\"");
59+
5760
if env::var("ORJSON_DISABLE_YYJSON").is_ok() {
5861
if env::var("CARGO_FEATURE_YYJSON").is_ok() {
5962
panic!("ORJSON_DISABLE_YYJSON and --features=yyjson both enabled.")

src/ffi/long.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22

33
// longintrepr.h, _longobject, _PyLongValue
44

5+
#[allow(dead_code)]
56
#[cfg(Py_3_12)]
67
#[allow(non_upper_case_globals)]
78
const SIGN_MASK: usize = 3;
89

9-
#[cfg(Py_3_12)]
10+
#[cfg(all(Py_3_12, feature = "inline_int"))]
1011
#[allow(non_upper_case_globals)]
1112
const SIGN_ZERO: usize = 1;
1213

13-
#[cfg(Py_3_12)]
14+
#[cfg(all(Py_3_12, feature = "inline_int"))]
1415
#[allow(non_upper_case_globals)]
1516
const NON_SIZE_BITS: usize = 3;
1617

@@ -28,6 +29,7 @@ pub struct PyLongObject {
2829
pub long_value: _PyLongValue,
2930
}
3031

32+
#[allow(dead_code)]
3133
#[cfg(not(Py_3_12))]
3234
#[repr(C)]
3335
pub struct PyLongObject {
@@ -47,30 +49,31 @@ pub fn pylong_is_unsigned(ptr: *mut pyo3_ffi::PyObject) -> bool {
4749
unsafe { (*(ptr as *mut pyo3_ffi::PyVarObject)).ob_size > 0 }
4850
}
4951

50-
#[cfg(Py_3_12)]
52+
#[cfg(all(Py_3_12, feature = "inline_int"))]
5153
#[inline(always)]
5254
pub fn pylong_fits_in_i32(ptr: *mut pyo3_ffi::PyObject) -> bool {
5355
unsafe { (*(ptr as *mut PyLongObject)).long_value.lv_tag < (2 << NON_SIZE_BITS) }
5456
}
5557

56-
#[cfg(not(Py_3_12))]
58+
#[cfg(all(not(Py_3_12), feature = "inline_int"))]
5759
#[inline(always)]
5860
pub fn pylong_fits_in_i32(ptr: *mut pyo3_ffi::PyObject) -> bool {
5961
unsafe { isize::abs((*(ptr as *mut pyo3_ffi::PyVarObject)).ob_size) == 1 }
6062
}
6163

62-
#[cfg(Py_3_12)]
64+
#[cfg(all(Py_3_12, feature = "inline_int"))]
6365
#[inline(always)]
6466
pub fn pylong_is_zero(ptr: *mut pyo3_ffi::PyObject) -> bool {
6567
unsafe { (*(ptr as *mut PyLongObject)).long_value.lv_tag & SIGN_MASK == SIGN_ZERO }
6668
}
67-
#[cfg(not(Py_3_12))]
69+
70+
#[cfg(all(not(Py_3_12), feature = "inline_int"))]
6871
#[inline(always)]
6972
pub fn pylong_is_zero(ptr: *mut pyo3_ffi::PyObject) -> bool {
7073
unsafe { (*(ptr as *mut pyo3_ffi::PyVarObject)).ob_size == 0 }
7174
}
7275

73-
#[cfg(Py_3_12)]
76+
#[cfg(all(Py_3_12, feature = "inline_int"))]
7477
#[inline(always)]
7578
pub fn pylong_get_inline_value(ptr: *mut pyo3_ffi::PyObject) -> i64 {
7679
unsafe {
@@ -82,7 +85,7 @@ pub fn pylong_get_inline_value(ptr: *mut pyo3_ffi::PyObject) -> i64 {
8285
}
8386
}
8487

85-
#[cfg(not(Py_3_12))]
88+
#[cfg(all(not(Py_3_12), feature = "inline_int"))]
8689
#[inline(always)]
8790
pub fn pylong_get_inline_value(ptr: *mut pyo3_ffi::PyObject) -> i64 {
8891
unsafe {

src/ffi/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ pub mod yyjson;
1010
pub use buffer::*;
1111
pub use bytes::*;
1212
pub use fragment::{orjson_fragmenttype_new, Fragment};
13-
pub use long::{pylong_fits_in_i32, pylong_get_inline_value, pylong_is_unsigned, pylong_is_zero};
13+
pub use long::pylong_is_unsigned;
14+
#[cfg(feature = "inline_int")]
15+
pub use long::{pylong_fits_in_i32, pylong_get_inline_value, pylong_is_zero};

src/serialize/per_type/int.rs

+44-12
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
22

3-
use crate::ffi::{pylong_fits_in_i32, pylong_get_inline_value, pylong_is_unsigned, pylong_is_zero};
43
use crate::opt::{Opt, STRICT_INTEGER};
54
use crate::serialize::error::SerializeError;
65
use serde::ser::{Serialize, Serializer};
76

8-
use core::ffi::c_uchar;
9-
use core::mem::transmute;
10-
117
// https://tools.ietf.org/html/rfc7159#section-6
128
// "[-(2**53)+1, (2**53)-1]"
139
const STRICT_INT_MIN: i64 = -9007199254740991;
@@ -29,26 +25,27 @@ impl IntSerializer {
2925

3026
impl Serialize for IntSerializer {
3127
#[inline(always)]
28+
#[cfg(feature = "inline_int")]
3229
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3330
where
3431
S: Serializer,
3532
{
3633
unsafe {
37-
if pylong_is_zero(self.ptr) {
34+
if crate::ffi::pylong_is_zero(self.ptr) {
3835
return serializer.serialize_bytes(b"0");
3936
}
40-
let is_signed = !pylong_is_unsigned(self.ptr) as i32;
41-
if pylong_fits_in_i32(self.ptr) {
37+
let is_signed = !crate::ffi::pylong_is_unsigned(self.ptr) as i32;
38+
if crate::ffi::pylong_fits_in_i32(self.ptr) {
4239
if is_signed == 0 {
43-
serializer.serialize_u64(pylong_get_inline_value(self.ptr) as u64)
40+
serializer.serialize_u64(crate::ffi::pylong_get_inline_value(self.ptr) as u64)
4441
} else {
45-
serializer.serialize_i64(pylong_get_inline_value(self.ptr) as i64)
42+
serializer.serialize_i64(crate::ffi::pylong_get_inline_value(self.ptr) as i64)
4643
}
4744
} else {
4845
let mut buffer: [u8; 8] = [0; 8];
4946
let ret = pyo3_ffi::_PyLong_AsByteArray(
5047
self.ptr as *mut pyo3_ffi::PyLongObject,
51-
buffer.as_mut_ptr() as *mut c_uchar,
48+
buffer.as_mut_ptr() as *mut core::ffi::c_uchar,
5249
8,
5350
1,
5451
is_signed,
@@ -58,15 +55,15 @@ impl Serialize for IntSerializer {
5855
err!(SerializeError::Integer64Bits)
5956
}
6057
if is_signed == 0 {
61-
let val = transmute::<[u8; 8], u64>(buffer);
58+
let val = core::mem::transmute::<[u8; 8], u64>(buffer);
6259
if unlikely!(opt_enabled!(self.opts, STRICT_INTEGER))
6360
&& val > STRICT_INT_MAX as u64
6461
{
6562
err!(SerializeError::Integer53Bits)
6663
}
6764
serializer.serialize_u64(val)
6865
} else {
69-
let val = transmute::<[u8; 8], i64>(buffer);
66+
let val = core::mem::transmute::<[u8; 8], i64>(buffer);
7067
if unlikely!(opt_enabled!(self.opts, STRICT_INTEGER))
7168
&& !(STRICT_INT_MIN..=STRICT_INT_MAX).contains(&val)
7269
{
@@ -77,4 +74,39 @@ impl Serialize for IntSerializer {
7774
}
7875
}
7976
}
77+
78+
#[inline(always)]
79+
#[cfg(not(feature = "inline_int"))]
80+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
81+
where
82+
S: Serializer,
83+
{
84+
unsafe {
85+
if crate::ffi::pylong_is_unsigned(self.ptr) {
86+
let val = ffi!(PyLong_AsUnsignedLongLong(self.ptr));
87+
if unlikely!(val == u64::MAX) && !ffi!(PyErr_Occurred()).is_null() {
88+
ffi!(PyErr_Clear());
89+
err!(SerializeError::Integer64Bits)
90+
} else if unlikely!(opt_enabled!(self.opts, STRICT_INTEGER))
91+
&& val > STRICT_INT_MAX as u64
92+
{
93+
err!(SerializeError::Integer53Bits)
94+
} else {
95+
serializer.serialize_u64(val)
96+
}
97+
} else {
98+
let val = ffi!(PyLong_AsLongLong(self.ptr));
99+
if unlikely!(val == -1) && !ffi!(PyErr_Occurred()).is_null() {
100+
ffi!(PyErr_Clear());
101+
err!(SerializeError::Integer64Bits)
102+
} else if unlikely!(opt_enabled!(self.opts, STRICT_INTEGER))
103+
&& !(STRICT_INT_MIN..=STRICT_INT_MAX).contains(&val)
104+
{
105+
err!(SerializeError::Integer53Bits)
106+
} else {
107+
serializer.serialize_i64(val)
108+
}
109+
}
110+
}
111+
}
80112
}

0 commit comments

Comments
 (0)