Skip to content

Commit

Permalink
Consolidate list and tuple iteration, fast path
Browse files Browse the repository at this point in the history
  • Loading branch information
ijl committed Dec 22, 2023
1 parent f82d20f commit 2b78849
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 88 deletions.
63 changes: 52 additions & 11 deletions src/serialize/per_type/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
use crate::opt::*;
use crate::serialize::error::SerializeError;
use crate::serialize::obtype::{pyobject_to_obtype, ObType};
use crate::serialize::per_type::{Date, DateTime, DateTimeBuffer, DateTimeLike, Time, UUID};
use crate::serialize::per_type::{
BoolSerializer, Date, DateTime, DateTimeBuffer, DateTimeLike, FloatSerializer, Int53Serializer,
IntSerializer, ListTupleSerializer, NoneSerializer, StrSerializer, Time, UUID,
};
use crate::serialize::serializer::PyObjectSerializer;
use crate::serialize::state::SerializerState;
use crate::str::{unicode_to_str, unicode_to_str_via_ffi};
use crate::typeref::{STR_TYPE, TRUE, VALUE_STR};
use crate::typeref::{
BOOL_TYPE, DATETIME_TYPE, DICT_TYPE, FLOAT_TYPE, INT_TYPE, LIST_TYPE, NONE_TYPE, STR_TYPE,
TRUE, VALUE_STR,
};
use compact_str::CompactString;
use serde::ser::{Serialize, SerializeMap, Serializer};
use smallvec::SmallVec;
Expand Down Expand Up @@ -73,7 +79,6 @@ impl Dict {
}
}
impl Serialize for Dict {
#[inline(always)]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
Expand Down Expand Up @@ -102,17 +107,53 @@ impl Serialize for Dict {
&mut next_value
));

if unlikely!(unsafe { ob_type!(key) != STR_TYPE }) {
err!(SerializeError::KeyMustBeStr)
// key
{
let key_ob_type = ob_type!(key);
if unlikely!(!is_class_by_type!(key_ob_type, STR_TYPE)) {
err!(SerializeError::KeyMustBeStr)
}
let key_as_str = unicode_to_str(key);
if unlikely!(key_as_str.is_none()) {
err!(SerializeError::InvalidStr)
}
map.serialize_key(key_as_str.unwrap()).unwrap();
}
let key_as_str = unicode_to_str(key);
if unlikely!(key_as_str.is_none()) {
err!(SerializeError::InvalidStr)

// value
{
let value_ob_type = ob_type!(value);
if is_class_by_type!(value_ob_type, STR_TYPE) {
map.serialize_value(&StrSerializer::new(value))?;
} else if is_class_by_type!(value_ob_type, INT_TYPE) {
if unlikely!(opt_enabled!(self.state.opts(), STRICT_INTEGER)) {
map.serialize_value(&Int53Serializer::new(value))?;
} else {
map.serialize_value(&IntSerializer::new(value))?;
}
} else if is_class_by_type!(value_ob_type, BOOL_TYPE) {
map.serialize_value(&BoolSerializer::new(value))?;
} else if is_class_by_type!(value_ob_type, NONE_TYPE) {
map.serialize_value(&NoneSerializer::new())?;
} else if is_class_by_type!(value_ob_type, FLOAT_TYPE) {
map.serialize_value(&FloatSerializer::new(value))?;
} else if is_class_by_type!(value_ob_type, DICT_TYPE) {
let pyvalue = DictGenericSerializer::new(value, self.state, self.default);
map.serialize_value(&pyvalue)?;
} else if is_class_by_type!(value_ob_type, LIST_TYPE) {
let pyvalue = ListTupleSerializer::from_list(value, self.state, self.default);
map.serialize_value(&pyvalue)?;
} else if is_class_by_type!(value_ob_type, DATETIME_TYPE)
&& opt_disabled!(self.state.opts(), PASSTHROUGH_DATETIME)
{
map.serialize_value(&DateTime::new(value, self.state.opts()))?;
} else {
let pyvalue = PyObjectSerializer::new(value, self.state, self.default);
map.serialize_value(&pyvalue)?;
}
}
let pyvalue = PyObjectSerializer::new(value, self.state, self.default);
map.serialize_key(key_as_str.unwrap()).unwrap();
map.serialize_value(&pyvalue)?;
}

map.end()
}
}
Expand Down
92 changes: 73 additions & 19 deletions src/serialize/per_type/list.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,109 @@
// SPDX-License-Identifier: (Apache-2.0 OR MIT)

use crate::opt::{PASSTHROUGH_DATETIME, STRICT_INTEGER};
use crate::serialize::error::SerializeError;
use crate::serialize::per_type::{
BoolSerializer, DateTime, DictGenericSerializer, FloatSerializer, Int53Serializer,
IntSerializer, NoneSerializer, StrSerializer,
};
use crate::serialize::serializer::PyObjectSerializer;
use crate::serialize::state::SerializerState;
use crate::typeref::*;

use serde::ser::{Serialize, SerializeSeq, Serializer};
use std::ptr::NonNull;

pub struct ListSerializer {
ptr: *mut pyo3_ffi::PyObject,
pub struct ListTupleSerializer {
data_ptr: *const *mut pyo3_ffi::PyObject,
state: SerializerState,
default: Option<NonNull<pyo3_ffi::PyObject>>,
len: usize,
}

impl ListSerializer {
pub fn new(
impl ListTupleSerializer {
pub fn from_list(
ptr: *mut pyo3_ffi::PyObject,
state: SerializerState,
default: Option<NonNull<pyo3_ffi::PyObject>>,
) -> Self {
ListSerializer {
ptr: ptr,
debug_assert!(
is_type!(ob_type!(ptr), LIST_TYPE)
|| is_subclass_by_flag!(ob_type!(ptr), Py_TPFLAGS_LIST_SUBCLASS)
);
let data_ptr = unsafe { (*(ptr as *mut pyo3_ffi::PyListObject)).ob_item };
let len = ffi!(Py_SIZE(ptr)) as usize;
Self {
data_ptr: data_ptr,
len: len,
state: state.copy_for_recursive_call(),
default: default,
}
}

pub fn from_tuple(
ptr: *mut pyo3_ffi::PyObject,
state: SerializerState,
default: Option<NonNull<pyo3_ffi::PyObject>>,
) -> Self {
debug_assert!(
is_type!(ob_type!(ptr), TUPLE_TYPE)
|| is_subclass_by_flag!(ob_type!(ptr), Py_TPFLAGS_TUPLE_SUBCLASS)
);
let data_ptr = unsafe { (*(ptr as *mut pyo3_ffi::PyTupleObject)).ob_item.as_ptr() };
let len = ffi!(Py_SIZE(ptr)) as usize;
Self {
data_ptr: data_ptr,
len: len,
state: state.copy_for_recursive_call(),
default: default,
}
}
}

impl Serialize for ListSerializer {
impl Serialize for ListTupleSerializer {
#[inline(never)]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if unlikely!(self.state.recursion_limit()) {
err!(SerializeError::RecursionLimit)
}
if ffi!(Py_SIZE(self.ptr)) == 0 {
if self.len == 0 {
serializer.serialize_seq(Some(0)).unwrap().end()
} else {
let mut seq = serializer.serialize_seq(None).unwrap();
for idx in 0..=ffi!(Py_SIZE(self.ptr)) - 1 {
let elem =
unsafe { *((*(self.ptr as *mut pyo3_ffi::PyListObject)).ob_item).offset(idx) };
let value = PyObjectSerializer::new(
elem,
self.opts,
self.default_calls,
self.recursion,
self.default,
);
seq.serialize_element(&value)?;
for idx in 0..=self.len - 1 {
let value = unsafe { *((self.data_ptr).add(idx)) };
let value_ob_type = ob_type!(value);
if is_class_by_type!(value_ob_type, STR_TYPE) {
seq.serialize_element(&StrSerializer::new(value))?;
} else if is_class_by_type!(value_ob_type, INT_TYPE) {
if unlikely!(opt_enabled!(self.state.opts(), STRICT_INTEGER)) {
seq.serialize_element(&Int53Serializer::new(value))?;
} else {
seq.serialize_element(&IntSerializer::new(value))?;
}
} else if is_class_by_type!(value_ob_type, BOOL_TYPE) {
seq.serialize_element(&BoolSerializer::new(value))?;
} else if is_class_by_type!(value_ob_type, NONE_TYPE) {
seq.serialize_element(&NoneSerializer::new())?;
} else if is_class_by_type!(value_ob_type, FLOAT_TYPE) {
seq.serialize_element(&FloatSerializer::new(value))?;
} else if is_class_by_type!(value_ob_type, DICT_TYPE) {
let pyvalue = DictGenericSerializer::new(value, self.state, self.default);
seq.serialize_element(&pyvalue)?;
} else if is_class_by_type!(value_ob_type, LIST_TYPE) {
let pyvalue = ListTupleSerializer::from_list(value, self.state, self.default);
seq.serialize_element(&pyvalue)?;
} else if is_class_by_type!(value_ob_type, DATETIME_TYPE)
&& opt_disabled!(self.state.opts(), PASSTHROUGH_DATETIME)
{
seq.serialize_element(&DateTime::new(value, self.state.opts()))?;
} else {
let pyvalue = PyObjectSerializer::new(value, self.state, self.default);
seq.serialize_element(&pyvalue)?;
}
}
seq.end()
}
Expand Down
4 changes: 1 addition & 3 deletions src/serialize/per_type/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ mod list;
mod none;
mod numpy;
mod pyenum;
mod tuple;
mod unicode;
mod uuid;

Expand All @@ -26,11 +25,10 @@ pub use dict::DictGenericSerializer;
pub use float::FloatSerializer;
pub use fragment::FragmentSerializer;
pub use int::{Int53Serializer, IntSerializer};
pub use list::ListSerializer;
pub use list::ListTupleSerializer;
pub use none::NoneSerializer;
pub use numpy::{is_numpy_array, is_numpy_scalar, NumpyScalar, NumpySerializer};
pub use pybool::BoolSerializer;
pub use pyenum::EnumSerializer;
pub use tuple::TupleSerializer;
pub use unicode::{StrSerializer, StrSubclassSerializer};
pub use uuid::UUID;
47 changes: 0 additions & 47 deletions src/serialize/per_type/tuple.rs

This file was deleted.

14 changes: 6 additions & 8 deletions src/serialize/serializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::serialize::obtype::{pyobject_to_obtype, ObType};
use crate::serialize::per_type::{
BoolSerializer, DataclassGenericSerializer, Date, DateTime, DefaultSerializer,
DictGenericSerializer, EnumSerializer, FloatSerializer, FragmentSerializer, Int53Serializer,
IntSerializer, ListSerializer, NoneSerializer, NumpyScalar, NumpySerializer, StrSerializer,
StrSubclassSerializer, Time, TupleSerializer, UUID,
IntSerializer, ListTupleSerializer, NoneSerializer, NumpyScalar, NumpySerializer,
StrSerializer, StrSubclassSerializer, Time, UUID,
};
use crate::serialize::state::SerializerState;
use crate::serialize::writer::*;
Expand Down Expand Up @@ -87,12 +87,10 @@ impl Serialize for PyObjectSerializer {
ObType::Dict => {
DictGenericSerializer::new(self.ptr, self.state, self.default).serialize(serializer)
}
ObType::List => {
ListSerializer::new(self.ptr, self.state, self.default).serialize(serializer)
}
ObType::Tuple => {
TupleSerializer::new(self.ptr, self.state, self.default).serialize(serializer)
}
ObType::List => ListTupleSerializer::from_list(self.ptr, self.state, self.default)
.serialize(serializer),
ObType::Tuple => ListTupleSerializer::from_tuple(self.ptr, self.state, self.default)
.serialize(serializer),
ObType::Dataclass => DataclassGenericSerializer::new(self).serialize(serializer),
ObType::Enum => EnumSerializer::new(self).serialize(serializer),
ObType::NumpyArray => NumpySerializer::new(self).serialize(serializer),
Expand Down

0 comments on commit 2b78849

Please sign in to comment.