From b0a8feadb7133ec43d9131bfd726df198f3b9a7e Mon Sep 17 00:00:00 2001 From: ijl Date: Wed, 29 Nov 2023 15:53:49 +0000 Subject: [PATCH] SerializeState, ObType, repr(transparent) --- src/opt.rs | 2 +- src/serialize/mod.rs | 2 + src/serialize/obtype.rs | 108 ++++++++++++++ src/serialize/per_type/dataclass.rs | 110 +++++---------- src/serialize/per_type/default.rs | 52 +++---- src/serialize/per_type/dict.rs | 119 ++++------------ src/serialize/per_type/list.rs | 18 +-- src/serialize/per_type/mod.rs | 1 + src/serialize/per_type/numpy.rs | 53 ++----- src/serialize/per_type/pyenum.rs | 45 ++---- src/serialize/per_type/tuple.rs | 22 +-- src/serialize/serializer.rs | 211 +++++----------------------- src/serialize/state.rs | 57 ++++++++ src/typeref.rs | 1 + 14 files changed, 318 insertions(+), 483 deletions(-) create mode 100644 src/serialize/obtype.rs create mode 100644 src/serialize/state.rs diff --git a/src/opt.rs b/src/opt.rs index 06963d62..4ceff66b 100644 --- a/src/opt.rs +++ b/src/opt.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) -pub type Opt = u16; +pub type Opt = u32; pub const INDENT_2: Opt = 1; pub const NAIVE_UTC: Opt = 1 << 1; diff --git a/src/serialize/mod.rs b/src/serialize/mod.rs index 53301410..9a0da4de 100644 --- a/src/serialize/mod.rs +++ b/src/serialize/mod.rs @@ -2,8 +2,10 @@ mod error; mod json; +mod obtype; mod per_type; mod serializer; +mod state; mod writer; pub use serializer::serialize; diff --git a/src/serialize/obtype.rs b/src/serialize/obtype.rs new file mode 100644 index 00000000..e526855e --- /dev/null +++ b/src/serialize/obtype.rs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +use crate::opt::{ + Opt, PASSTHROUGH_DATACLASS, PASSTHROUGH_DATETIME, PASSTHROUGH_SUBCLASS, SERIALIZE_NUMPY, +}; +use crate::serialize::per_type::{is_numpy_array, is_numpy_scalar}; +use crate::typeref::{ + BOOL_TYPE, DATACLASS_FIELDS_STR, DATETIME_TYPE, DATE_TYPE, DICT_TYPE, ENUM_TYPE, FLOAT_TYPE, + FRAGMENT_TYPE, INT_TYPE, LIST_TYPE, NONE_TYPE, STR_TYPE, TIME_TYPE, TUPLE_TYPE, UUID_TYPE, +}; + +#[repr(u32)] +pub enum ObType { + Str, + Int, + Bool, + None, + Float, + List, + Dict, + Datetime, + Date, + Time, + Tuple, + Uuid, + Dataclass, + NumpyScalar, + NumpyArray, + Enum, + StrSubclass, + Fragment, + Unknown, +} + +pub fn pyobject_to_obtype(obj: *mut pyo3_ffi::PyObject, opts: Opt) -> ObType { + let ob_type = ob_type!(obj); + if is_class_by_type!(ob_type, STR_TYPE) { + ObType::Str + } else if is_class_by_type!(ob_type, INT_TYPE) { + ObType::Int + } else if is_class_by_type!(ob_type, BOOL_TYPE) { + ObType::Bool + } else if is_class_by_type!(ob_type, NONE_TYPE) { + ObType::None + } else if is_class_by_type!(ob_type, FLOAT_TYPE) { + ObType::Float + } else if is_class_by_type!(ob_type, LIST_TYPE) { + ObType::List + } else if is_class_by_type!(ob_type, DICT_TYPE) { + ObType::Dict + } else if is_class_by_type!(ob_type, DATETIME_TYPE) && opt_disabled!(opts, PASSTHROUGH_DATETIME) + { + ObType::Datetime + } else { + pyobject_to_obtype_unlikely(ob_type, opts) + } +} + +#[cfg_attr(feature = "optimize", optimize(size))] +#[inline(never)] +pub fn pyobject_to_obtype_unlikely(ob_type: *mut pyo3_ffi::PyTypeObject, opts: Opt) -> ObType { + if is_class_by_type!(ob_type, UUID_TYPE) { + return ObType::Uuid; + } else if is_class_by_type!(ob_type, TUPLE_TYPE) { + return ObType::Tuple; + } else if is_class_by_type!(ob_type, FRAGMENT_TYPE) { + return ObType::Fragment; + } + + if opt_disabled!(opts, PASSTHROUGH_DATETIME) { + if is_class_by_type!(ob_type, DATE_TYPE) { + return ObType::Date; + } else if is_class_by_type!(ob_type, TIME_TYPE) { + return ObType::Time; + } + } + + if opt_disabled!(opts, PASSTHROUGH_SUBCLASS) { + if is_subclass_by_flag!(ob_type, Py_TPFLAGS_UNICODE_SUBCLASS) { + return ObType::StrSubclass; + } else if is_subclass_by_flag!(ob_type, Py_TPFLAGS_LONG_SUBCLASS) { + return ObType::Int; + } else if is_subclass_by_flag!(ob_type, Py_TPFLAGS_LIST_SUBCLASS) { + return ObType::List; + } else if is_subclass_by_flag!(ob_type, Py_TPFLAGS_DICT_SUBCLASS) { + return ObType::Dict; + } + } + + if is_subclass_by_type!(ob_type, ENUM_TYPE) { + return ObType::Enum; + } + + if opt_disabled!(opts, PASSTHROUGH_DATACLASS) && pydict_contains!(ob_type, DATACLASS_FIELDS_STR) + { + return ObType::Dataclass; + } + + if unlikely!(opt_enabled!(opts, SERIALIZE_NUMPY)) { + if is_numpy_scalar(ob_type) { + return ObType::NumpyScalar; + } else if is_numpy_array(ob_type) { + return ObType::NumpyArray; + } + } + + ObType::Unknown +} diff --git a/src/serialize/per_type/dataclass.rs b/src/serialize/per_type/dataclass.rs index 9d90d94d..a80e65d3 100644 --- a/src/serialize/per_type/dataclass.rs +++ b/src/serialize/per_type/dataclass.rs @@ -1,82 +1,60 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) -use crate::opt::*; use crate::serialize::error::SerializeError; -use crate::serialize::serializer::{PyObjectSerializer, RECURSION_LIMIT}; +use crate::serialize::serializer::PyObjectSerializer; +use crate::serialize::state::SerializerState; use crate::str::unicode_to_str; -use crate::typeref::*; +use crate::typeref::{ + DATACLASS_FIELDS_STR, DICT_STR, FIELD_TYPE, FIELD_TYPE_STR, SLOTS_STR, STR_TYPE, +}; use serde::ser::{Serialize, SerializeMap, Serializer}; use std::ptr::NonNull; -pub struct DataclassGenericSerializer { - ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, - default: Option>, +#[repr(transparent)] +pub struct DataclassGenericSerializer<'a> { + previous: &'a PyObjectSerializer, } -impl DataclassGenericSerializer { - pub fn new( - ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, - default: Option>, - ) -> Self { - DataclassGenericSerializer { - ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion + 1, - default: default, - } +impl<'a> DataclassGenericSerializer<'a> { + pub fn new(previous: &'a PyObjectSerializer) -> Self { + Self { previous: previous } } } -impl Serialize for DataclassGenericSerializer { +impl<'a> Serialize for DataclassGenericSerializer<'a> { #[inline(never)] fn serialize(&self, serializer: S) -> Result where S: Serializer, { - if unlikely!(self.recursion == RECURSION_LIMIT) { + if unlikely!(self.previous.state.recursion_limit()) { err!(SerializeError::RecursionLimit) } - let dict = ffi!(PyObject_GetAttr(self.ptr, DICT_STR)); - let ob_type = ob_type!(self.ptr); + let dict = ffi!(PyObject_GetAttr(self.previous.ptr, DICT_STR)); + let ob_type = ob_type!(self.previous.ptr); if unlikely!(dict.is_null()) { ffi!(PyErr_Clear()); DataclassFallbackSerializer::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, + self.previous.ptr, + self.previous.state, + self.previous.default, ) .serialize(serializer) } else if pydict_contains!(ob_type, SLOTS_STR) { let ret = DataclassFallbackSerializer::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, + self.previous.ptr, + self.previous.state, + self.previous.default, ) .serialize(serializer); ffi!(Py_DECREF(dict)); ret } else { - let ret = DataclassFastSerializer::new( - dict, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer); + let ret = + DataclassFastSerializer::new(dict, self.previous.state, self.previous.default) + .serialize(serializer); ffi!(Py_DECREF(dict)); ret } @@ -85,25 +63,19 @@ impl Serialize for DataclassGenericSerializer { pub struct DataclassFastSerializer { ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, } impl DataclassFastSerializer { pub fn new( ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, ) -> Self { DataclassFastSerializer { ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion, + state: state.copy_for_recursive_call(), default: default, } } @@ -152,13 +124,7 @@ impl Serialize for DataclassFastSerializer { if unlikely!(key_as_str.as_bytes()[0] == b'_') { continue; } - let pyvalue = PyObjectSerializer::new( - value, - self.opts, - self.default_calls, - self.recursion, - self.default, - ); + let pyvalue = PyObjectSerializer::new(value, self.state, self.default); map.serialize_key(key_as_str).unwrap(); map.serialize_value(&pyvalue)?; } @@ -168,25 +134,19 @@ impl Serialize for DataclassFastSerializer { pub struct DataclassFallbackSerializer { ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, } impl DataclassFallbackSerializer { pub fn new( ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, ) -> Self { DataclassFallbackSerializer { ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion, + state: state.copy_for_recursive_call(), default: default, } } @@ -246,13 +206,7 @@ impl Serialize for DataclassFallbackSerializer { let value = ffi!(PyObject_GetAttr(self.ptr, attr)); debug_assert!(ffi!(Py_REFCNT(value)) >= 2); ffi!(Py_DECREF(value)); - let pyvalue = PyObjectSerializer::new( - value, - self.opts, - self.default_calls, - self.recursion, - self.default, - ); + let pyvalue = PyObjectSerializer::new(value, self.state, self.default); map.serialize_key(key_as_str).unwrap(); map.serialize_value(&pyvalue)? diff --git a/src/serialize/per_type/default.rs b/src/serialize/per_type/default.rs index 3c143b1b..7b08ab9b 100644 --- a/src/serialize/per_type/default.rs +++ b/src/serialize/per_type/default.rs @@ -1,82 +1,62 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) -use crate::opt::*; use crate::serialize::error::SerializeError; -use crate::serialize::serializer::{PyObjectSerializer, RECURSION_LIMIT}; +use crate::serialize::serializer::PyObjectSerializer; use serde::ser::{Serialize, Serializer}; -use std::ptr::NonNull; - -pub struct DefaultSerializer { - ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, - default: Option>, +#[repr(transparent)] +pub struct DefaultSerializer<'a> { + previous: &'a PyObjectSerializer, } -impl DefaultSerializer { - pub fn new( - ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, - default: Option>, - ) -> Self { - DefaultSerializer { - ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion, - default: default, - } +impl<'a> DefaultSerializer<'a> { + pub fn new(previous: &'a PyObjectSerializer) -> Self { + Self { previous: previous } } } -impl Serialize for DefaultSerializer { +impl<'a> Serialize for DefaultSerializer<'a> { #[inline(never)] #[cfg_attr(feature = "optimize", optimize(size))] fn serialize(&self, serializer: S) -> Result where S: Serializer, { - match self.default { + match self.previous.default { Some(callable) => { - if unlikely!(self.default_calls == RECURSION_LIMIT) { + if unlikely!(self.previous.state.default_calls_limit()) { err!(SerializeError::DefaultRecursionLimit) } #[cfg(not(Py_3_10))] let default_obj = ffi!(PyObject_CallFunctionObjArgs( callable.as_ptr(), - self.ptr, + self.previous.ptr, std::ptr::null_mut() as *mut pyo3_ffi::PyObject )); #[cfg(Py_3_10)] let default_obj = unsafe { pyo3_ffi::PyObject_Vectorcall( callable.as_ptr(), - std::ptr::addr_of!(self.ptr), + std::ptr::addr_of!(self.previous.ptr), pyo3_ffi::PyVectorcall_NARGS(1) as usize, std::ptr::null_mut(), ) }; if unlikely!(default_obj.is_null()) { - err!(SerializeError::UnsupportedType(nonnull!(self.ptr))) + err!(SerializeError::UnsupportedType(nonnull!(self.previous.ptr))) } else { let res = PyObjectSerializer::new( default_obj, - self.opts, - self.default_calls + 1, - self.recursion, - self.default, + self.previous.state.copy_for_default_call(), + self.previous.default, ) .serialize(serializer); ffi!(Py_DECREF(default_obj)); res } } - None => err!(SerializeError::UnsupportedType(nonnull!(self.ptr))), + None => err!(SerializeError::UnsupportedType(nonnull!(self.previous.ptr))), } } } diff --git a/src/serialize/per_type/dict.rs b/src/serialize/per_type/dict.rs index 34b3b0b8..38211316 100644 --- a/src/serialize/per_type/dict.rs +++ b/src/serialize/per_type/dict.rs @@ -2,13 +2,12 @@ use crate::opt::*; use crate::serialize::error::SerializeError; -use crate::serialize::per_type::datetimelike::{DateTimeBuffer, DateTimeLike}; -use crate::serialize::per_type::*; -use crate::serialize::serializer::{ - pyobject_to_obtype, ObType, PyObjectSerializer, RECURSION_LIMIT, -}; +use crate::serialize::obtype::{pyobject_to_obtype, ObType}; +use crate::serialize::per_type::{Date, DateTime, DateTimeBuffer, DateTimeLike, 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::*; +use crate::typeref::{STR_TYPE, TRUE, VALUE_STR}; use compact_str::CompactString; use serde::ser::{Serialize, SerializeMap, Serializer}; use smallvec::SmallVec; @@ -16,25 +15,19 @@ use std::ptr::NonNull; pub struct DictGenericSerializer { ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, } impl DictGenericSerializer { pub fn new( ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, ) -> Self { DictGenericSerializer { ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion + 1, + state: state.copy_for_recursive_call(), default: default, } } @@ -46,62 +39,35 @@ impl Serialize for DictGenericSerializer { where S: Serializer, { - if unlikely!(self.recursion == RECURSION_LIMIT) { + if unlikely!(self.state.recursion_limit()) { err!(SerializeError::RecursionLimit) } if unlikely!(ffi!(Py_SIZE(self.ptr)) == 0) { serializer.serialize_map(Some(0)).unwrap().end() - } else if opt_disabled!(self.opts, SORT_OR_NON_STR_KEYS) { - Dict::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer) - } else if opt_enabled!(self.opts, NON_STR_KEYS) { - DictNonStrKey::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer) + } else if opt_disabled!(self.state.opts(), SORT_OR_NON_STR_KEYS) { + Dict::new(self.ptr, self.state, self.default).serialize(serializer) + } else if opt_enabled!(self.state.opts(), NON_STR_KEYS) { + DictNonStrKey::new(self.ptr, self.state, self.default).serialize(serializer) } else { - DictSortedKey::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer) + DictSortedKey::new(self.ptr, self.state, self.default).serialize(serializer) } } } pub struct Dict { ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, } impl Dict { pub fn new( ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, ) -> Self { Dict { ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion, + state: state, default: default, } } @@ -143,13 +109,7 @@ impl Serialize for Dict { if unlikely!(key_as_str.is_none()) { err!(SerializeError::InvalidStr) } - let pyvalue = PyObjectSerializer::new( - value, - self.opts, - self.default_calls, - self.recursion, - self.default, - ); + let pyvalue = PyObjectSerializer::new(value, self.state, self.default); map.serialize_key(key_as_str.unwrap()).unwrap(); map.serialize_value(&pyvalue)?; } @@ -159,25 +119,19 @@ impl Serialize for Dict { pub struct DictSortedKey { ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, } impl DictSortedKey { pub fn new( ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, ) -> Self { DictSortedKey { ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion, + state: state, default: default, } } @@ -229,13 +183,7 @@ impl Serialize for DictSortedKey { let mut map = serializer.serialize_map(None).unwrap(); for (key, val) in items.iter() { - let pyvalue = PyObjectSerializer::new( - *val, - self.opts, - self.default_calls, - self.recursion, - self.default, - ); + let pyvalue = PyObjectSerializer::new(*val, self.state, self.default); map.serialize_key(key).unwrap(); map.serialize_value(&pyvalue)?; } @@ -245,25 +193,19 @@ impl Serialize for DictSortedKey { pub struct DictNonStrKey { ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, } impl DictNonStrKey { pub fn new( ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, ) -> Self { DictNonStrKey { ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion, + state: state, default: default, } } @@ -379,7 +321,8 @@ impl Serialize for DictNonStrKey { let len = ffi!(Py_SIZE(self.ptr)) as usize; let mut items: SmallVec<[(CompactString, *mut pyo3_ffi::PyObject); 8]> = SmallVec::with_capacity(len); - let opts = self.opts & NOT_PASSTHROUGH; + + let opts = self.state.opts() & NOT_PASSTHROUGH; let mut next_key: *mut pyo3_ffi::PyObject = std::ptr::null_mut(); let mut next_value: *mut pyo3_ffi::PyObject = std::ptr::null_mut(); @@ -423,13 +366,7 @@ impl Serialize for DictNonStrKey { let mut map = serializer.serialize_map(None).unwrap(); for (key, val) in items.iter() { - let pyvalue = PyObjectSerializer::new( - *val, - self.opts, - self.default_calls, - self.recursion, - self.default, - ); + let pyvalue = PyObjectSerializer::new(*val, self.state, self.default); map.serialize_key(key).unwrap(); map.serialize_value(&pyvalue)?; } diff --git a/src/serialize/per_type/list.rs b/src/serialize/per_type/list.rs index 5c8790e6..ed6d235e 100644 --- a/src/serialize/per_type/list.rs +++ b/src/serialize/per_type/list.rs @@ -1,33 +1,27 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) -use crate::opt::Opt; use crate::serialize::error::SerializeError; -use crate::serialize::serializer::{PyObjectSerializer, RECURSION_LIMIT}; +use crate::serialize::serializer::PyObjectSerializer; +use crate::serialize::state::SerializerState; use serde::ser::{Serialize, SerializeSeq, Serializer}; use std::ptr::NonNull; pub struct ListSerializer { ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, } impl ListSerializer { pub fn new( ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, ) -> Self { ListSerializer { ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion + 1, + state: state.copy_for_recursive_call(), default: default, } } @@ -38,7 +32,7 @@ impl Serialize for ListSerializer { where S: Serializer, { - if unlikely!(self.recursion == RECURSION_LIMIT) { + if unlikely!(self.state.recursion_limit()) { err!(SerializeError::RecursionLimit) } if ffi!(Py_SIZE(self.ptr)) == 0 { diff --git a/src/serialize/per_type/mod.rs b/src/serialize/per_type/mod.rs index 6ab62530..3265b08f 100644 --- a/src/serialize/per_type/mod.rs +++ b/src/serialize/per_type/mod.rs @@ -20,6 +20,7 @@ mod uuid; pub use dataclass::DataclassGenericSerializer; pub use datetime::{Date, DateTime, Time}; +pub use datetimelike::{DateTimeBuffer, DateTimeError, DateTimeLike, Offset}; pub use default::DefaultSerializer; pub use dict::DictGenericSerializer; pub use float::FloatSerializer; diff --git a/src/serialize/per_type/numpy.rs b/src/serialize/per_type/numpy.rs index 3632f3bf..9f129c56 100644 --- a/src/serialize/per_type/numpy.rs +++ b/src/serialize/per_type/numpy.rs @@ -1,10 +1,10 @@ use crate::opt::*; use crate::serialize::error::SerializeError; -use crate::serialize::per_type::datetimelike::{ - DateTimeBuffer, DateTimeError, DateTimeLike, Offset, +use crate::serialize::per_type::{ + DateTimeBuffer, DateTimeError, DateTimeLike, DefaultSerializer, Offset, }; -use crate::serialize::per_type::*; +use crate::serialize::serializer::PyObjectSerializer; use crate::typeref::{load_numpy_types, ARRAY_STRUCT_STR, DESCR_STR, DTYPE_STR, NUMPY_TYPES}; use chrono::{Datelike, NaiveDate, NaiveDateTime, Timelike}; use pyo3_ffi::*; @@ -12,35 +12,19 @@ use serde::ser::{self, Serialize, SerializeSeq, Serializer}; use std::convert::TryInto; use std::fmt; use std::os::raw::{c_char, c_int, c_void}; -use std::ptr::NonNull; -pub struct NumpySerializer { - ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, - default: Option>, -} - -impl NumpySerializer { - pub fn new( - ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, - default: Option>, - ) -> Self { - NumpySerializer { - ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion, - default: default, - } +#[repr(transparent)] +pub struct NumpySerializer<'a> { + previous: &'a PyObjectSerializer, +} + +impl<'a> NumpySerializer<'a> { + pub fn new(previous: &'a PyObjectSerializer) -> Self { + Self { previous: previous } } } -impl Serialize for NumpySerializer { +impl<'a> Serialize for NumpySerializer<'a> { #[cold] #[inline(never)] #[cfg_attr(feature = "optimize", optimize(size))] @@ -48,20 +32,13 @@ impl Serialize for NumpySerializer { where S: Serializer, { - match NumpyArray::new(self.ptr, self.opts) { + match NumpyArray::new(self.previous.ptr, self.previous.state.opts()) { Ok(val) => val.serialize(serializer), Err(PyArrayError::Malformed) => err!(SerializeError::NumpyMalformed), Err(PyArrayError::NotContiguous) | Err(PyArrayError::UnsupportedDataType) - if self.default.is_some() => + if self.previous.default.is_some() => { - DefaultSerializer::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer) + DefaultSerializer::new(self.previous).serialize(serializer) } Err(PyArrayError::NotContiguous) => { err!(SerializeError::NumpyNotCContiguous) diff --git a/src/serialize/per_type/pyenum.rs b/src/serialize/per_type/pyenum.rs index 06baaffd..d84064d6 100644 --- a/src/serialize/per_type/pyenum.rs +++ b/src/serialize/per_type/pyenum.rs @@ -1,53 +1,30 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) -use crate::opt::*; use crate::serialize::serializer::PyObjectSerializer; -use crate::typeref::*; +use crate::typeref::VALUE_STR; use serde::ser::{Serialize, Serializer}; -use std::ptr::NonNull; -pub struct EnumSerializer { - ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, - default: Option>, +#[repr(transparent)] +pub struct EnumSerializer<'a> { + previous: &'a PyObjectSerializer, } -impl EnumSerializer { - pub fn new( - ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, - default: Option>, - ) -> Self { - EnumSerializer { - ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion, - default: default, - } +impl<'a> EnumSerializer<'a> { + pub fn new(previous: &'a PyObjectSerializer) -> Self { + Self { previous: previous } } } -impl Serialize for EnumSerializer { +impl<'a> Serialize for EnumSerializer<'a> { #[inline(never)] fn serialize(&self, serializer: S) -> Result where S: Serializer, { - let value = ffi!(PyObject_GetAttr(self.ptr, VALUE_STR)); + let value = ffi!(PyObject_GetAttr(self.previous.ptr, VALUE_STR)); debug_assert!(ffi!(Py_REFCNT(value)) >= 2); - let ret = PyObjectSerializer::new( - value, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer); + let ret = PyObjectSerializer::new(value, self.previous.state, self.previous.default) + .serialize(serializer); ffi!(Py_DECREF(value)); ret } diff --git a/src/serialize/per_type/tuple.rs b/src/serialize/per_type/tuple.rs index 4f23abb6..e85f040e 100644 --- a/src/serialize/per_type/tuple.rs +++ b/src/serialize/per_type/tuple.rs @@ -1,32 +1,26 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) -use crate::opt::Opt; use crate::serialize::serializer::PyObjectSerializer; +use crate::serialize::state::SerializerState; use serde::ser::{Serialize, SerializeSeq, Serializer}; use std::ptr::NonNull; pub struct TupleSerializer { ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, } impl TupleSerializer { pub fn new( ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, ) -> Self { TupleSerializer { ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion + 1, + state: state.copy_for_recursive_call(), default: default, } } @@ -44,13 +38,7 @@ impl Serialize for TupleSerializer { let mut seq = serializer.serialize_seq(None).unwrap(); for i in 0..=ffi!(Py_SIZE(self.ptr)) as usize - 1 { let elem = ffi!(PyTuple_GET_ITEM(self.ptr, i as isize)); - let value = PyObjectSerializer::new( - elem, - self.opts, - self.default_calls, - self.recursion, - self.default, - ); + let value = PyObjectSerializer::new(elem, self.state, self.default); seq.serialize_element(&value)?; } seq.end() diff --git a/src/serialize/serializer.rs b/src/serialize/serializer.rs index 24f0464e..bf4928c2 100644 --- a/src/serialize/serializer.rs +++ b/src/serialize/serializer.rs @@ -1,24 +1,28 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) -use crate::opt::*; -use crate::serialize::per_type::*; +use crate::opt::{Opt, APPEND_NEWLINE, INDENT_2, STRICT_INTEGER}; +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, +}; +use crate::serialize::state::SerializerState; use crate::serialize::writer::*; -use crate::typeref::*; use serde::ser::{Serialize, Serializer}; use std::io::Write; use std::ptr::NonNull; use crate::serialize::json::{to_writer, to_writer_pretty}; -pub const RECURSION_LIMIT: u8 = 255; - pub fn serialize( ptr: *mut pyo3_ffi::PyObject, default: Option>, opts: Opt, ) -> Result, String> { let mut buf = BytesWriter::default(); - let obj = PyObjectSerializer::new(ptr, opts, 0, 0, default); + let obj = PyObjectSerializer::new(ptr, SerializerState::new(opts), default); let res = if opt_disabled!(opts, INDENT_2) { to_writer(&mut buf, &obj) } else { @@ -38,125 +42,21 @@ pub fn serialize( } } -#[repr(u32)] -pub enum ObType { - Str, - Int, - Bool, - None, - Float, - List, - Dict, - Datetime, - Date, - Time, - Tuple, - Uuid, - Dataclass, - NumpyScalar, - NumpyArray, - Enum, - StrSubclass, - Fragment, - Unknown, -} - -pub fn pyobject_to_obtype(obj: *mut pyo3_ffi::PyObject, opts: Opt) -> ObType { - let ob_type = ob_type!(obj); - if is_class_by_type!(ob_type, STR_TYPE) { - ObType::Str - } else if is_class_by_type!(ob_type, INT_TYPE) { - ObType::Int - } else if is_class_by_type!(ob_type, BOOL_TYPE) { - ObType::Bool - } else if is_class_by_type!(ob_type, NONE_TYPE) { - ObType::None - } else if is_class_by_type!(ob_type, FLOAT_TYPE) { - ObType::Float - } else if is_class_by_type!(ob_type, LIST_TYPE) { - ObType::List - } else if is_class_by_type!(ob_type, DICT_TYPE) { - ObType::Dict - } else if is_class_by_type!(ob_type, DATETIME_TYPE) && opt_disabled!(opts, PASSTHROUGH_DATETIME) - { - ObType::Datetime - } else { - pyobject_to_obtype_unlikely(ob_type, opts) - } -} - -#[cfg_attr(feature = "optimize", optimize(size))] -#[inline(never)] -pub fn pyobject_to_obtype_unlikely(ob_type: *mut pyo3_ffi::PyTypeObject, opts: Opt) -> ObType { - if is_class_by_type!(ob_type, UUID_TYPE) { - return ObType::Uuid; - } else if is_class_by_type!(ob_type, TUPLE_TYPE) { - return ObType::Tuple; - } else if is_class_by_type!(ob_type, FRAGMENT_TYPE) { - return ObType::Fragment; - } - - if opt_disabled!(opts, PASSTHROUGH_DATETIME) { - if is_class_by_type!(ob_type, DATE_TYPE) { - return ObType::Date; - } else if is_class_by_type!(ob_type, TIME_TYPE) { - return ObType::Time; - } - } - - if opt_disabled!(opts, PASSTHROUGH_SUBCLASS) { - if is_subclass_by_flag!(ob_type, Py_TPFLAGS_UNICODE_SUBCLASS) { - return ObType::StrSubclass; - } else if is_subclass_by_flag!(ob_type, Py_TPFLAGS_LONG_SUBCLASS) { - return ObType::Int; - } else if is_subclass_by_flag!(ob_type, Py_TPFLAGS_LIST_SUBCLASS) { - return ObType::List; - } else if is_subclass_by_flag!(ob_type, Py_TPFLAGS_DICT_SUBCLASS) { - return ObType::Dict; - } - } - - if is_subclass_by_type!(ob_type, ENUM_TYPE) { - return ObType::Enum; - } - - if opt_disabled!(opts, PASSTHROUGH_DATACLASS) && pydict_contains!(ob_type, DATACLASS_FIELDS_STR) - { - return ObType::Dataclass; - } - - if unlikely!(opt_enabled!(opts, SERIALIZE_NUMPY)) { - if is_numpy_scalar(ob_type) { - return ObType::NumpyScalar; - } else if is_numpy_array(ob_type) { - return ObType::NumpyArray; - } - } - - ObType::Unknown -} - pub struct PyObjectSerializer { - ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, - default: Option>, + pub ptr: *mut pyo3_ffi::PyObject, + pub state: SerializerState, + pub default: Option>, } impl PyObjectSerializer { pub fn new( ptr: *mut pyo3_ffi::PyObject, - opts: Opt, - default_calls: u8, - recursion: u8, + state: SerializerState, default: Option>, ) -> Self { PyObjectSerializer { ptr: ptr, - opts: opts, - default_calls: default_calls, - recursion: recursion, + state: state, default: default, } } @@ -167,11 +67,11 @@ impl Serialize for PyObjectSerializer { where S: Serializer, { - match pyobject_to_obtype(self.ptr, self.opts) { + match pyobject_to_obtype(self.ptr, self.state.opts()) { ObType::Str => StrSerializer::new(self.ptr).serialize(serializer), ObType::StrSubclass => StrSubclassSerializer::new(self.ptr).serialize(serializer), ObType::Int => { - if unlikely!(opt_enabled!(self.opts, STRICT_INTEGER)) { + if unlikely!(opt_enabled!(self.state.opts(), STRICT_INTEGER)) { Int53Serializer::new(self.ptr).serialize(serializer) } else { IntSerializer::new(self.ptr).serialize(serializer) @@ -180,68 +80,27 @@ impl Serialize for PyObjectSerializer { ObType::None => NoneSerializer::new().serialize(serializer), ObType::Float => FloatSerializer::new(self.ptr).serialize(serializer), ObType::Bool => BoolSerializer::new(self.ptr).serialize(serializer), - ObType::Datetime => DateTime::new(self.ptr, self.opts).serialize(serializer), + ObType::Datetime => DateTime::new(self.ptr, self.state.opts()).serialize(serializer), ObType::Date => Date::new(self.ptr).serialize(serializer), - ObType::Time => Time::new(self.ptr, self.opts).serialize(serializer), + ObType::Time => Time::new(self.ptr, self.state.opts()).serialize(serializer), ObType::Uuid => UUID::new(self.ptr).serialize(serializer), - ObType::Dict => DictGenericSerializer::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer), - ObType::List => ListSerializer::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer), - ObType::Tuple => TupleSerializer::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer), - ObType::Dataclass => DataclassGenericSerializer::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer), - ObType::Enum => EnumSerializer::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer), - ObType::NumpyArray => NumpySerializer::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer), - ObType::NumpyScalar => NumpyScalar::new(self.ptr, self.opts).serialize(serializer), + 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::Dataclass => DataclassGenericSerializer::new(self).serialize(serializer), + ObType::Enum => EnumSerializer::new(self).serialize(serializer), + ObType::NumpyArray => NumpySerializer::new(self).serialize(serializer), + ObType::NumpyScalar => { + NumpyScalar::new(self.ptr, self.state.opts()).serialize(serializer) + } ObType::Fragment => FragmentSerializer::new(self.ptr).serialize(serializer), - ObType::Unknown => DefaultSerializer::new( - self.ptr, - self.opts, - self.default_calls, - self.recursion, - self.default, - ) - .serialize(serializer), + ObType::Unknown => DefaultSerializer::new(self).serialize(serializer), } } } diff --git a/src/serialize/state.rs b/src/serialize/state.rs new file mode 100644 index 00000000..f0c6c737 --- /dev/null +++ b/src/serialize/state.rs @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +use crate::opt::*; + +const RECURSION_MASK: u32 = 255 << 24; + +const DEFAULT_MASK: u32 = 255 << 16; + +#[repr(transparent)] +#[derive(Copy, Clone)] +pub struct SerializerState { + // recursion: u8, + // default_calls: u8, + // opts: Opt, + state: u32, +} + +impl SerializerState { + #[inline(always)] + pub fn new(opts: Opt) -> Self { + debug_assert!(opts < u16::MAX as u32); + Self { state: opts } + } + + #[inline(always)] + pub fn opts(&self) -> u32 { + self.state + } + + #[inline(always)] + pub fn recursion_limit(&self) -> bool { + self.state & RECURSION_MASK == RECURSION_MASK + } + + #[inline(always)] + pub fn default_calls_limit(&self) -> bool { + self.state & DEFAULT_MASK == DEFAULT_MASK + } + + #[inline(always)] + pub fn copy_for_recursive_call(&self) -> Self { + let opt = self.state & !RECURSION_MASK; + let recursion = (((self.state & RECURSION_MASK) >> 24) + 1) << 24; + Self { + state: opt | recursion, + } + } + + #[inline(always)] + pub fn copy_for_default_call(&self) -> Self { + let opt = self.state & !DEFAULT_MASK; + let default_calls = (((self.state & DEFAULT_MASK) >> 16) + 1) << 16; + Self { + state: opt | default_calls, + } + } +} diff --git a/src/typeref.rs b/src/typeref.rs index 242a8275..ec096492 100644 --- a/src/typeref.rs +++ b/src/typeref.rs @@ -148,6 +148,7 @@ pub fn init_typerefs() { #[cfg_attr(feature = "optimize", optimize(size))] fn _init_typerefs_impl() -> bool { unsafe { + debug_assert!(crate::opt::MAX_OPT < u16::MAX as i32); assert!(crate::deserialize::KEY_MAP .set(crate::deserialize::KeyMap::default()) .is_ok());