Skip to content

Commit 41018c8

Browse files
committed
PyLongObject small value inline
1 parent 323a930 commit 41018c8

File tree

4 files changed

+75
-11
lines changed

4 files changed

+75
-11
lines changed

src/ffi/long.rs

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,89 @@
66
const SIGN_MASK: usize = 3;
77
#[cfg(Py_3_12)]
88
const SIGN_ZERO: usize = 1;
9+
10+
#[cfg(Py_3_12)]
11+
#[allow(non_upper_case_globals)]
12+
const _PyLong_NON_SIZE_BITS: usize = 3;
13+
914
#[cfg(Py_3_12)]
10-
const SIGN_POSITIVE: usize = 0;
15+
#[repr(C)]
16+
struct _PyLongValue {
17+
pub lv_tag: usize,
18+
pub ob_digit: u32,
19+
}
1120

1221
#[cfg(Py_3_12)]
13-
#[allow(dead_code)]
22+
#[repr(C)]
1423
struct PyLongObject {
1524
pub ob_refcnt: pyo3_ffi::Py_ssize_t,
1625
pub ob_type: *mut pyo3_ffi::PyTypeObject,
17-
pub lv_tag: usize,
18-
pub ob_digit: u8,
26+
pub long_value: _PyLongValue,
1927
}
2028

2129
#[cfg(Py_3_12)]
30+
#[inline(always)]
2231
pub fn pylong_is_zero(ptr: *mut pyo3_ffi::PyObject) -> bool {
23-
unsafe { (*(ptr as *mut PyLongObject)).lv_tag & SIGN_MASK == SIGN_ZERO }
32+
unsafe { (*(ptr as *mut PyLongObject)).long_value.lv_tag & SIGN_MASK == SIGN_ZERO }
2433
}
2534

2635
#[cfg(not(Py_3_12))]
36+
#[inline(always)]
2737
pub fn pylong_is_zero(ptr: *mut pyo3_ffi::PyObject) -> bool {
2838
unsafe { (*(ptr as *mut pyo3_ffi::PyVarObject)).ob_size == 0 }
2939
}
3040

3141
#[cfg(Py_3_12)]
42+
#[inline(always)]
3243
pub fn pylong_is_unsigned(ptr: *mut pyo3_ffi::PyObject) -> bool {
33-
unsafe { (*(ptr as *mut PyLongObject)).lv_tag & SIGN_MASK == SIGN_POSITIVE }
44+
unsafe {
45+
1 - (((*(ptr as *mut PyLongObject)).long_value.lv_tag & _PyLong_NON_SIZE_BITS) as isize) > 0
46+
}
3447
}
3548

3649
#[cfg(not(Py_3_12))]
50+
#[inline(always)]
3751
pub fn pylong_is_unsigned(ptr: *mut pyo3_ffi::PyObject) -> bool {
3852
unsafe { (*(ptr as *mut pyo3_ffi::PyVarObject)).ob_size > 0 }
3953
}
54+
55+
#[cfg(Py_3_12)]
56+
#[inline(always)]
57+
fn pylong_is_compact(ptr: *mut pyo3_ffi::PyObject) -> bool {
58+
unsafe { (*(ptr as *mut PyLongObject)).long_value.lv_tag < (2 << _PyLong_NON_SIZE_BITS) }
59+
}
60+
61+
#[cfg(Py_3_12)]
62+
#[inline(always)]
63+
pub fn pylong_value_unsigned(ptr: *mut pyo3_ffi::PyObject) -> u64 {
64+
if pylong_is_compact(ptr) == true {
65+
unsafe { (*(ptr as *mut PyLongObject)).long_value.ob_digit as u64 }
66+
} else {
67+
ffi!(PyLong_AsUnsignedLongLong(ptr))
68+
}
69+
}
70+
71+
#[cfg(not(Py_3_12))]
72+
#[inline(always)]
73+
pub fn pylong_value_unsigned(ptr: *mut pyo3_ffi::PyObject) -> u64 {
74+
ffi!(PyLong_AsUnsignedLongLong(ptr))
75+
}
76+
77+
#[cfg(not(Py_3_12))]
78+
#[inline(always)]
79+
pub fn pylong_value_signed(ptr: *mut pyo3_ffi::PyObject) -> i64 {
80+
ffi!(PyLong_AsLongLong(ptr))
81+
}
82+
83+
#[cfg(Py_3_12)]
84+
#[inline(always)]
85+
pub fn pylong_value_signed(ptr: *mut pyo3_ffi::PyObject) -> i64 {
86+
if pylong_is_compact(ptr) == true {
87+
unsafe {
88+
let sign = 1 - ((*(ptr as *mut PyLongObject)).long_value.lv_tag & SIGN_MASK) as i64;
89+
sign * (*(ptr as *mut PyLongObject)).long_value.ob_digit as i64
90+
}
91+
} else {
92+
ffi!(PyLong_AsLongLong(ptr))
93+
}
94+
}

src/ffi/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ pub mod yyjson;
1010
pub use buffer::*;
1111
pub use bytes::*;
1212
pub use fragment::{orjson_fragmenttype_new, Fragment};
13-
pub use long::{pylong_is_unsigned, pylong_is_zero};
13+
pub use long::{pylong_is_unsigned, pylong_is_zero, pylong_value_signed, pylong_value_unsigned};

src/serialize/per_type/int.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
22

3-
use crate::ffi::{pylong_is_unsigned, pylong_is_zero};
3+
use crate::ffi::{pylong_is_unsigned, pylong_is_zero, pylong_value_signed, pylong_value_unsigned};
44
use crate::serialize::error::SerializeError;
55
use serde::ser::{Serialize, Serializer};
66

@@ -28,14 +28,14 @@ impl Serialize for IntSerializer {
2828
if pylong_is_zero(self.ptr) {
2929
serializer.serialize_u64(0)
3030
} else if pylong_is_unsigned(self.ptr) {
31-
let val = ffi!(PyLong_AsUnsignedLongLong(self.ptr));
31+
let val = pylong_value_unsigned(self.ptr);
3232
if unlikely!(val == u64::MAX) && !ffi!(PyErr_Occurred()).is_null() {
3333
err!(SerializeError::Integer64Bits)
3434
} else {
3535
serializer.serialize_u64(val)
3636
}
3737
} else {
38-
let val = ffi!(PyLong_AsLongLong(self.ptr));
38+
let val = pylong_value_signed(self.ptr);
3939
if unlikely!(val == -1) && !ffi!(PyErr_Occurred()).is_null() {
4040
err!(SerializeError::Integer64Bits)
4141
}
@@ -62,7 +62,7 @@ impl Serialize for Int53Serializer {
6262
where
6363
S: Serializer,
6464
{
65-
let val = ffi!(PyLong_AsLongLong(self.ptr));
65+
let val = pylong_value_signed(self.ptr);
6666
if unlikely!(val == -1) {
6767
if ffi!(PyErr_Occurred()).is_null() {
6868
serializer.serialize_i64(val)

test/test_type.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,15 @@ def test_none(self):
329329
assert orjson.dumps(obj) == ref.encode("utf-8")
330330
assert orjson.loads(ref) == obj
331331

332+
def test_int(self):
333+
"""
334+
int compact and non-compact
335+
"""
336+
obj = [-5000, -1000, -10, -5, -2, -1, 0, 1, 2, 5, 10, 1000, 50000]
337+
ref = b"[-5000,-1000,-10,-5,-2,-1,0,1,2,5,10,1000,50000]"
338+
assert orjson.dumps(obj) == ref
339+
assert orjson.loads(ref) == obj
340+
332341
def test_null_array(self):
333342
"""
334343
null array

0 commit comments

Comments
 (0)