use super::collection::{
compatible_list_array_field, read_primitive_array_vec_mismatch, read_vec_compatible_mismatch,
};
use crate::context::{ReadContext, WriteContext};
use crate::error::Error;
use crate::meta::{FieldInfo, FieldType};
use crate::resolver::{RefFlag, RefMode, TypeResolver};
use crate::serializer::{core::read_value_type_info, primitive_list, Serializer};
use crate::type_id::{self, TypeId, SIZE_OF_REF_AND_TYPE, UNKNOWN};
use std::any::Any;
use std::borrow::Cow;
use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::Arc;
#[doc(hidden)]
pub use super::arc::ArcCodec;
#[doc(hidden)]
pub use super::array::ArrayCodec;
#[doc(hidden)]
pub use super::box_::BoxCodec;
#[doc(hidden)]
pub use super::heap::BinaryHeapCodec;
#[doc(hidden)]
pub use super::list::{LinkedListCodec, VecDequeCodec};
#[doc(hidden)]
pub use super::map::{BTreeMapCodec, HashMapCodec};
#[doc(hidden)]
pub use super::mutex::MutexCodec;
#[doc(hidden)]
pub use super::rc::RcCodec;
#[doc(hidden)]
pub use super::refcell::RefCellCodec;
#[doc(hidden)]
pub use super::set::{BTreeSetCodec, HashSetCodec};
#[doc(hidden)]
pub use super::tuple::{
Tuple10Codec, Tuple11Codec, Tuple12Codec, Tuple13Codec, Tuple14Codec, Tuple15Codec,
Tuple16Codec, Tuple17Codec, Tuple18Codec, Tuple19Codec, Tuple1Codec, Tuple20Codec,
Tuple21Codec, Tuple22Codec, Tuple2Codec, Tuple3Codec, Tuple4Codec, Tuple5Codec, Tuple6Codec,
Tuple7Codec, Tuple8Codec, Tuple9Codec,
};
#[doc(hidden)]
pub use super::weak::{ArcWeakCodec, RcWeakCodec};
#[inline(always)]
pub fn field_ref_mode(field_type: &FieldType) -> RefMode {
if field_type.track_ref {
RefMode::Tracking
} else if crate::serializer::util::field_need_write_ref_into(
field_type.type_id,
field_type.nullable,
) {
RefMode::NullOnly
} else {
RefMode::None
}
}
#[inline(always)]
pub(super) fn field_type_with_ref_flags(
field_type: &FieldType,
nullable: bool,
track_ref: bool,
) -> Cow<'_, FieldType> {
if field_type.nullable == nullable && field_type.track_ref == track_ref {
Cow::Borrowed(field_type)
} else {
let mut adjusted = field_type.clone();
adjusted.nullable = nullable;
adjusted.track_ref = track_ref;
Cow::Owned(adjusted)
}
}
#[inline(always)]
fn field_read_type_info<T: Serializer>(context: &ReadContext, field_type: &FieldType) -> bool {
if context.is_compatible() {
crate::serializer::util::field_need_read_type_info(field_type.type_id)
} else {
T::IS_POLYMORPHIC
}
}
#[inline(always)]
fn field_write_type_info<T: Serializer>(context: &WriteContext) -> bool {
if context.is_compatible() {
crate::serializer::util::field_need_write_type_info(T::static_type_id())
} else {
T::IS_POLYMORPHIC
}
}
#[inline(always)]
fn serializer_static_field_type_id<T: Serializer>() -> u32 {
let type_id = T::static_type_id() as u32;
if type_id == TypeId::TYPED_UNION as u32 || type_id == TypeId::NAMED_UNION as u32 {
TypeId::UNION as u32
} else {
type_id
}
}
#[cold]
#[inline(never)]
fn carrier_field_alias_error() -> Error {
Error::type_error(
"carrier serializers selected by a field must use canonical carrier syntax so derive can compose child codecs",
)
}
#[inline(always)]
fn provider_field_type<S: Serializer, const NULLABLE: bool, const TRACK_REF: bool>(
type_resolver: &TypeResolver,
) -> Result<FieldType, Error> {
let static_type_id = serializer_static_field_type_id::<S>();
if matches!(
static_type_id,
type_id::LIST | type_id::SET | type_id::MAP | type_id::BINARY
) || type_id::PRIMITIVE_ARRAY_TYPES.contains(&static_type_id)
{
return Err(carrier_field_alias_error());
}
if type_resolver.is_xlang() && static_type_id == TypeId::UNION as u32 {
return Ok(FieldType::new_with_ref(
TypeId::UNION as u32,
NULLABLE,
TRACK_REF,
Vec::new(),
));
}
if static_type_id != TypeId::UNION as u32 && type_id::is_internal_type(static_type_id) {
return Ok(FieldType::new_with_ref(
static_type_id,
NULLABLE,
TRACK_REF,
Vec::new(),
));
}
let type_info = type_resolver
.get_provider_type_info(&std::any::TypeId::of::<S>())
.map_err(Error::enhance_type_error::<S>)?;
let mut type_id = type_info.get_type_id() as u32;
let mut user_type_id = type_info.get_user_type_id();
if type_resolver.is_xlang()
&& (type_id == TypeId::TYPED_UNION as u32 || type_id == TypeId::NAMED_UNION as u32)
{
type_id = TypeId::UNION as u32;
user_type_id = u32::MAX;
} else if type_id::is_internal_type(type_id) {
user_type_id = u32::MAX;
}
Ok(FieldType::new_with_user_type_id(
type_id,
user_type_id,
NULLABLE,
TRACK_REF,
Vec::new(),
))
}
#[inline(always)]
fn serializer_ref_mode<T: Serializer, const NULLABLE: bool, const TRACK_REF: bool>() -> RefMode {
if TRACK_REF {
RefMode::Tracking
} else if crate::serializer::util::field_need_write_ref_into(
serializer_static_field_type_id::<T>(),
NULLABLE,
) {
RefMode::NullOnly
} else {
RefMode::None
}
}
#[inline(always)]
fn serializer_read_type_info<T: Serializer>(context: &ReadContext) -> bool {
if context.is_compatible() {
crate::serializer::util::field_need_read_type_info(serializer_static_field_type_id::<T>())
} else {
T::IS_POLYMORPHIC
}
}
#[inline(always)]
pub(super) fn codec_read_type_info<T, C>(context: &ReadContext, field_type: &FieldType) -> bool
where
T: 'static,
C: Codec<T>,
{
if context.is_compatible() {
field_type.type_id == type_id::UNKNOWN
|| crate::serializer::util::field_need_read_type_info(field_type.type_id)
} else {
C::IS_POLYMORPHIC
}
}
#[inline(always)]
fn codec_static_field_type_id<T, C>() -> u32
where
T: 'static,
C: Codec<T>,
{
let type_id = C::static_type_id() as u32;
if type_id == TypeId::TYPED_UNION as u32 || type_id == TypeId::NAMED_UNION as u32 {
TypeId::UNION as u32
} else {
type_id
}
}
#[inline(always)]
pub(super) fn codec_read_type_info_static<T, C>(context: &ReadContext) -> bool
where
T: 'static,
C: Codec<T>,
{
if context.is_compatible() {
let type_id = codec_static_field_type_id::<T, C>();
type_id == type_id::UNKNOWN || crate::serializer::util::field_need_read_type_info(type_id)
} else {
C::IS_POLYMORPHIC
}
}
#[inline(always)]
pub(super) fn codec_write_type_info<T, C>(context: &WriteContext) -> bool
where
T: 'static,
C: Codec<T>,
{
if context.is_compatible() {
crate::serializer::util::field_need_write_type_info(C::static_type_id())
} else {
C::IS_POLYMORPHIC
}
}
#[inline(always)]
pub(super) fn codec_ref_mode<T, C, const NULLABLE: bool, const TRACK_REF: bool>() -> RefMode
where
T: 'static,
C: Codec<T>,
{
if TRACK_REF {
RefMode::Tracking
} else if crate::serializer::util::field_need_write_ref_into(
codec_static_field_type_id::<T, C>(),
NULLABLE,
) {
RefMode::NullOnly
} else {
RefMode::None
}
}
#[inline(always)]
pub(super) fn same_numeric_family(local: u32, remote: u32) -> bool {
matches!(
(local, remote),
(type_id::INT32, type_id::INT32 | type_id::VARINT32)
| (type_id::VARINT32, type_id::INT32 | type_id::VARINT32)
| (
type_id::INT64,
type_id::INT64 | type_id::VARINT64 | type_id::TAGGED_INT64
)
| (
type_id::VARINT64,
type_id::INT64 | type_id::VARINT64 | type_id::TAGGED_INT64
)
| (
type_id::TAGGED_INT64,
type_id::INT64 | type_id::VARINT64 | type_id::TAGGED_INT64
)
| (type_id::UINT32, type_id::UINT32 | type_id::VAR_UINT32)
| (type_id::VAR_UINT32, type_id::UINT32 | type_id::VAR_UINT32)
| (
type_id::UINT64,
type_id::UINT64 | type_id::VAR_UINT64 | type_id::TAGGED_UINT64
)
| (
type_id::VAR_UINT64,
type_id::UINT64 | type_id::VAR_UINT64 | type_id::TAGGED_UINT64
)
| (
type_id::TAGGED_UINT64,
type_id::UINT64 | type_id::VAR_UINT64 | type_id::TAGGED_UINT64
)
)
}
#[inline(always)]
pub(super) fn allows_missing_generics(type_id: u32) -> bool {
type_id == type_id::LIST || type_id == type_id::SET || type_id == type_id::MAP
}
#[inline(always)]
pub fn field_types_compatible(local: &FieldType, remote: &FieldType) -> bool {
local.exact_shape_match(remote)
}
#[inline(always)]
fn compatible_byte_sequence_field(local: &FieldType, remote: &FieldType) -> bool {
!local.track_ref
&& !remote.track_ref
&& local.nullable == remote.nullable
&& ((local.type_id == type_id::BINARY && remote.type_id == type_id::UINT8_ARRAY)
|| (local.type_id == type_id::UINT8_ARRAY && remote.type_id == type_id::BINARY))
}
#[cold]
#[inline(never)]
pub fn compatible_field_pair(local: &FieldType, remote: &FieldType) -> bool {
field_types_compatible(local, remote)
|| compatible_byte_sequence_field(local, remote)
|| crate::meta::compatible_scalar_field_pair(local, remote)
|| compatible_list_array_field(local, remote)
|| local.compatible_shape_match(remote)
}
macro_rules! compatible_scalar_reader {
($read:ident, $read_option:ident, $target:ident, $target_option:ident, $ty:ty) => {
#[inline(always)]
pub fn $read(
context: &mut ReadContext,
local_type: u32,
remote_field: &FieldInfo,
) -> Result<$ty, Error> {
super::scalar_conversion::$target(context, local_type, remote_field)
}
#[inline(always)]
pub fn $read_option(
context: &mut ReadContext,
local_type: u32,
remote_field: &FieldInfo,
) -> Result<Option<$ty>, Error> {
super::scalar_conversion::$target_option(context, local_type, remote_field)
}
};
}
compatible_scalar_reader!(
read_bool_compatible_scalar,
read_bool_option_compatible_scalar,
read_bool_target,
read_bool_option_target,
bool
);
compatible_scalar_reader!(
read_string_compatible_scalar,
read_string_option_compatible_scalar,
read_string_target,
read_string_option_target,
String
);
compatible_scalar_reader!(
read_i8_compatible_scalar,
read_i8_option_compatible_scalar,
read_i8_target,
read_i8_option_target,
i8
);
compatible_scalar_reader!(
read_i16_compatible_scalar,
read_i16_option_compatible_scalar,
read_i16_target,
read_i16_option_target,
i16
);
compatible_scalar_reader!(
read_i32_compatible_scalar,
read_i32_option_compatible_scalar,
read_i32_target,
read_i32_option_target,
i32
);
compatible_scalar_reader!(
read_i64_compatible_scalar,
read_i64_option_compatible_scalar,
read_i64_target,
read_i64_option_target,
i64
);
compatible_scalar_reader!(
read_u8_compatible_scalar,
read_u8_option_compatible_scalar,
read_u8_target,
read_u8_option_target,
u8
);
compatible_scalar_reader!(
read_u16_compatible_scalar,
read_u16_option_compatible_scalar,
read_u16_target,
read_u16_option_target,
u16
);
compatible_scalar_reader!(
read_u32_compatible_scalar,
read_u32_option_compatible_scalar,
read_u32_target,
read_u32_option_target,
u32
);
compatible_scalar_reader!(
read_u64_compatible_scalar,
read_u64_option_compatible_scalar,
read_u64_target,
read_u64_option_target,
u64
);
compatible_scalar_reader!(
read_f32_compatible_scalar,
read_f32_option_compatible_scalar,
read_f32_target,
read_f32_option_target,
f32
);
compatible_scalar_reader!(
read_f64_compatible_scalar,
read_f64_option_compatible_scalar,
read_f64_target,
read_f64_option_target,
f64
);
compatible_scalar_reader!(
read_float16_compatible_scalar,
read_float16_option_compatible_scalar,
read_float16_target,
read_float16_option_target,
crate::types::float16::float16
);
compatible_scalar_reader!(
read_bfloat16_compatible_scalar,
read_bfloat16_option_compatible_scalar,
read_bfloat16_target,
read_bfloat16_option_target,
crate::types::bfloat16::bfloat16
);
compatible_scalar_reader!(
read_decimal_compatible_scalar,
read_decimal_option_compatible_scalar,
read_decimal_target,
read_decimal_option_target,
crate::types::Decimal
);
#[cold]
#[inline(never)]
fn missing_generic_type(owner: &str, index: usize) -> Error {
Error::invalid_data(format!(
"{owner} field metadata is missing generic type at index {index}"
))
}
#[inline(always)]
pub(super) fn generic_field_type<'a>(
field_type: &'a FieldType,
index: usize,
owner: &str,
) -> Result<&'a FieldType, Error> {
field_type
.generics
.get(index)
.ok_or_else(|| missing_generic_type(owner, index))
}
pub enum CodecReadType {
Field(FieldType),
TypeInfo(Rc<crate::TypeInfo>),
}
pub trait Codec<T: 'static>: Serializer<Target = T> {
fn field_type(type_resolver: &TypeResolver) -> Result<FieldType, Error>;
#[inline(always)]
fn field_reserved_space() -> usize {
<Self as Serializer>::reserved_space()
}
fn write_field(value: &T, context: &mut WriteContext) -> Result<(), Error>;
fn read_field(context: &mut ReadContext) -> Result<T, Error>;
#[inline(always)]
fn read_compatible(
context: &mut ReadContext,
local_field_type: &FieldType,
remote_field_type: &FieldType,
) -> Result<Option<T>, Error> {
if field_types_compatible(local_field_type, remote_field_type)
|| local_field_type.compatible_shape_match(remote_field_type)
{
return Self::read_field_with_type(context, remote_field_type).map(Some);
}
super::scalar_conversion::read_scalar_field::<T, Self>(
context,
local_field_type,
remote_field_type,
)
}
#[inline(always)]
fn read_data_with_type(
context: &mut ReadContext,
_remote_data_type: &FieldType,
) -> Result<T, Error> {
<Self as Serializer>::read_data(context)
}
#[inline(always)]
fn read_data_with_type_info(
context: &mut ReadContext,
type_info: &Rc<crate::TypeInfo>,
) -> Result<T, Error> {
<Self as Serializer>::read_with_type_info(context, RefMode::None, type_info)
}
#[inline(always)]
fn type_info_exact(
_context: &ReadContext,
_type_info: &Rc<crate::TypeInfo>,
) -> Result<bool, Error> {
Ok(false)
}
fn read_field_with_type(
context: &mut ReadContext,
remote_field_type: &FieldType,
) -> Result<T, Error>;
fn write_with_mode(
value: &T,
context: &mut WriteContext,
ref_mode: RefMode,
write_type_info: bool,
has_generics: bool,
) -> Result<(), Error>;
#[doc(hidden)]
#[inline(always)]
fn write_with_type_info(
value: &T,
context: &mut WriteContext,
ref_mode: RefMode,
type_info: &Rc<crate::TypeInfo>,
has_generics: bool,
) -> Result<(), Error> {
let _ = type_info;
Self::write_with_mode(value, context, ref_mode, false, has_generics)
}
#[inline(always)]
fn read_type_info_value(context: &mut ReadContext) -> Result<CodecReadType, Error> {
Self::read_type_info_as_field_type(context).map(CodecReadType::Field)
}
#[inline(always)]
fn read_type_info_as_field_type(context: &mut ReadContext) -> Result<FieldType, Error> {
<Self as Serializer>::read_type_info(context)?;
Self::field_type(context.get_type_resolver())
}
}
pub struct SerializerCodec<S, const NULLABLE: bool, const TRACK_REF: bool>(PhantomData<fn() -> S>);
impl<S, const NULLABLE: bool, const TRACK_REF: bool> Serializer
for SerializerCodec<S, NULLABLE, TRACK_REF>
where
S: Serializer,
{
type Target = S::Target;
#[inline(always)]
fn write_data(value: &S::Target, context: &mut WriteContext) -> Result<(), Error> {
S::write_data(value, context)
}
#[inline(always)]
fn read_data(context: &mut ReadContext) -> Result<S::Target, Error> {
S::read_data(context)
}
#[inline(always)]
fn default_value(context: &mut ReadContext) -> Result<S::Target, Error> {
S::default_value(context)
}
#[inline(always)]
fn write(
value: &S::Target,
context: &mut WriteContext,
ref_mode: RefMode,
write_type_info: bool,
) -> Result<(), Error> {
S::write(value, context, ref_mode, write_type_info)
}
#[inline(always)]
fn write_type_info_value(
context: &mut WriteContext,
target_type_id: std::any::TypeId,
) -> Result<Rc<crate::TypeInfo>, Error> {
S::write_type_info_value(context, target_type_id)
}
#[inline(always)]
fn write_with_type_info(
value: &S::Target,
context: &mut WriteContext,
ref_mode: RefMode,
type_info: &Rc<crate::TypeInfo>,
) -> Result<(), Error> {
S::write_with_type_info(value, context, ref_mode, type_info)
}
#[inline(always)]
fn read(
context: &mut ReadContext,
ref_mode: RefMode,
read_type_info: bool,
) -> Result<S::Target, Error> {
S::read(context, ref_mode, read_type_info)
}
#[inline(always)]
fn read_with_type_info(
context: &mut ReadContext,
ref_mode: RefMode,
type_info: &Rc<crate::TypeInfo>,
) -> Result<S::Target, Error> {
S::read_with_type_info(context, ref_mode, type_info)
}
#[inline(always)]
fn write_type_info(context: &mut WriteContext) -> Result<(), Error> {
S::write_type_info(context)
}
#[inline(always)]
fn read_type_info(context: &mut ReadContext) -> Result<(), Error> {
S::read_type_info(context)
}
#[inline(always)]
fn read_arc_any(context: &mut ReadContext) -> Result<Arc<dyn Any + Send + Sync>, Error> {
S::read_arc_any(context)
}
#[inline(always)]
fn static_type_id() -> TypeId {
S::static_type_id()
}
#[inline(always)]
fn reserved_space() -> usize {
S::reserved_space()
}
const IS_OPTIONAL: bool = S::IS_OPTIONAL;
const IS_POLYMORPHIC: bool = S::IS_POLYMORPHIC;
const IS_SHARED_REF: bool = S::IS_SHARED_REF;
const IS_WRAPPER: bool = S::IS_WRAPPER;
const REQUIRES_SCOPED_ACCESS: bool = S::REQUIRES_SCOPED_ACCESS;
const READ_DATA_ALWAYS_ADVANCES: bool = S::READ_DATA_ALWAYS_ADVANCES;
#[inline(always)]
fn is_none(value: &S::Target) -> bool {
S::is_none(value)
}
#[inline(always)]
fn dynamic_type_id(value: &S::Target) -> Result<Option<std::any::TypeId>, Error> {
S::dynamic_type_id(value)
}
}
impl<S, const NULLABLE: bool, const TRACK_REF: bool> Codec<S::Target>
for SerializerCodec<S, NULLABLE, TRACK_REF>
where
S: Serializer,
{
#[inline(always)]
fn field_type(type_resolver: &TypeResolver) -> Result<FieldType, Error> {
provider_field_type::<S, NULLABLE, TRACK_REF>(type_resolver)
}
#[inline(always)]
fn field_reserved_space() -> usize {
S::reserved_space() + SIZE_OF_REF_AND_TYPE
}
#[inline(always)]
fn write_field(value: &S::Target, context: &mut WriteContext) -> Result<(), Error> {
S::write(
value,
context,
serializer_ref_mode::<S, NULLABLE, TRACK_REF>(),
field_write_type_info::<S>(context),
)
}
#[cfg_attr(debug_assertions, inline(never))]
#[cfg_attr(not(debug_assertions), inline(always))]
fn read_field(context: &mut ReadContext) -> Result<S::Target, Error> {
let ref_mode = serializer_ref_mode::<S, NULLABLE, TRACK_REF>();
let read_type_info = serializer_read_type_info::<S>(context);
if ref_mode == RefMode::None && !S::IS_POLYMORPHIC {
if read_type_info {
if let Some(type_info) = read_value_type_info::<S>(context)? {
return Self::read_data_with_type_info(context, &type_info);
}
}
return S::read_data(context);
}
S::read(context, ref_mode, read_type_info)
}
#[inline(always)]
fn read_compatible(
context: &mut ReadContext,
local_field_type: &FieldType,
remote_field_type: &FieldType,
) -> Result<Option<S::Target>, Error> {
if field_types_compatible(local_field_type, remote_field_type)
|| local_field_type.compatible_shape_match(remote_field_type)
{
return Self::read_field_with_type(context, remote_field_type).map(Some);
}
super::scalar_conversion::read_scalar_field::<S::Target, Self>(
context,
local_field_type,
remote_field_type,
)
}
#[inline(always)]
fn read_data_with_type(
context: &mut ReadContext,
_remote_data_type: &FieldType,
) -> Result<S::Target, Error> {
S::read_data(context)
}
#[inline(always)]
fn read_data_with_type_info(
context: &mut ReadContext,
type_info: &Rc<crate::TypeInfo>,
) -> Result<S::Target, Error> {
if Self::type_info_exact(context, type_info)? {
return S::read_data(context);
}
S::read_with_type_info(context, RefMode::None, type_info)
}
#[inline(always)]
fn type_info_exact(
context: &ReadContext,
type_info: &Rc<crate::TypeInfo>,
) -> Result<bool, Error> {
if !context.is_compatible() {
return Ok(false);
}
Ok(type_info.has_exact_local_schema())
}
#[cfg_attr(debug_assertions, inline(never))]
#[cfg_attr(not(debug_assertions), inline(always))]
fn read_field_with_type(
context: &mut ReadContext,
remote_field_type: &FieldType,
) -> Result<S::Target, Error> {
let ref_mode = field_ref_mode(remote_field_type);
let read_type_info = field_read_type_info::<S>(context, remote_field_type);
if ref_mode == RefMode::None && !S::IS_POLYMORPHIC {
if read_type_info {
if let Some(type_info) = read_value_type_info::<S>(context)? {
return Self::read_data_with_type_info(context, &type_info);
}
}
return S::read_data(context);
}
S::read(context, ref_mode, read_type_info)
}
#[inline(always)]
fn write_with_mode(
value: &S::Target,
context: &mut WriteContext,
ref_mode: RefMode,
write_type_info: bool,
_has_generics: bool,
) -> Result<(), Error> {
S::write(value, context, ref_mode, write_type_info)
}
#[inline(always)]
fn write_with_type_info(
value: &S::Target,
context: &mut WriteContext,
ref_mode: RefMode,
type_info: &Rc<crate::TypeInfo>,
_has_generics: bool,
) -> Result<(), Error> {
S::write_with_type_info(value, context, ref_mode, type_info)
}
#[inline(always)]
fn read_type_info_value(context: &mut ReadContext) -> Result<CodecReadType, Error> {
if let Some(type_info) = read_value_type_info::<S>(context)? {
return Ok(CodecReadType::TypeInfo(type_info));
}
Self::field_type(context.get_type_resolver()).map(CodecReadType::Field)
}
}
pub struct OptionCodec<T, C, const TRACK_REF: bool>(PhantomData<(T, C)>);
#[cold]
#[inline(never)]
fn missing_option_value() -> Error {
Error::invalid_data("Option::None cannot be written as non-null data")
}
impl<T, C, const TRACK_REF: bool> Serializer for OptionCodec<T, C, TRACK_REF>
where
T: 'static,
C: Serializer<Target = T>,
{
type Target = Option<T>;
const READ_DATA_ALWAYS_ADVANCES: bool = C::READ_DATA_ALWAYS_ADVANCES;
#[inline(always)]
fn reserved_space() -> usize {
C::reserved_space() + 1
}
#[inline(always)]
fn write_data(value: &Option<T>, context: &mut WriteContext) -> Result<(), Error> {
let value = value.as_ref().ok_or_else(missing_option_value)?;
C::write_data(value, context)
}
#[inline(always)]
fn read_data(context: &mut ReadContext) -> Result<Option<T>, Error> {
Ok(Some(C::read_data(context)?))
}
#[inline(always)]
fn write(
value: &Option<T>,
context: &mut WriteContext,
ref_mode: RefMode,
write_type_info: bool,
) -> Result<(), Error> {
match ref_mode {
RefMode::None => {
let value = value.as_ref().ok_or_else(missing_option_value)?;
C::write(value, context, RefMode::None, write_type_info)
}
RefMode::NullOnly => {
if let Some(value) = value {
context.writer.write_i8(RefFlag::NotNullValue as i8);
C::write(value, context, RefMode::None, write_type_info)
} else {
context.writer.write_i8(RefFlag::Null as i8);
Ok(())
}
}
RefMode::Tracking => {
if let Some(value) = value {
C::write(value, context, RefMode::Tracking, write_type_info)
} else {
context.writer.write_i8(RefFlag::Null as i8);
Ok(())
}
}
}
}
#[inline(always)]
fn write_type_info_value(
context: &mut WriteContext,
target_type_id: std::any::TypeId,
) -> Result<Rc<crate::TypeInfo>, Error> {
C::write_type_info_value(context, target_type_id)
}
#[inline(always)]
fn write_with_type_info(
value: &Option<T>,
context: &mut WriteContext,
ref_mode: RefMode,
type_info: &Rc<crate::TypeInfo>,
) -> Result<(), Error> {
match ref_mode {
RefMode::None => {
let value = value.as_ref().ok_or_else(missing_option_value)?;
C::write_with_type_info(value, context, RefMode::None, type_info)
}
RefMode::NullOnly => {
if let Some(value) = value {
context.writer.write_i8(RefFlag::NotNullValue as i8);
C::write_with_type_info(value, context, RefMode::None, type_info)
} else {
context.writer.write_i8(RefFlag::Null as i8);
Ok(())
}
}
RefMode::Tracking => {
if let Some(value) = value {
C::write_with_type_info(value, context, RefMode::Tracking, type_info)
} else {
context.writer.write_i8(RefFlag::Null as i8);
Ok(())
}
}
}
}
#[inline(always)]
fn read(
context: &mut ReadContext,
ref_mode: RefMode,
read_type_info: bool,
) -> Result<Option<T>, Error> {
match ref_mode {
RefMode::None => Ok(Some(C::read(context, RefMode::None, read_type_info)?)),
RefMode::NullOnly => {
let ref_flag = context.reader.read_i8()?;
if ref_flag == RefFlag::Null as i8 {
return Ok(None);
}
Ok(Some(C::read(context, RefMode::None, read_type_info)?))
}
RefMode::Tracking => {
let ref_flag = context.reader.read_i8()?;
if ref_flag == RefFlag::Null as i8 {
return Ok(None);
}
context.reader.move_back(1);
Ok(Some(C::read(context, RefMode::Tracking, read_type_info)?))
}
}
}
#[inline(always)]
fn read_with_type_info(
context: &mut ReadContext,
ref_mode: RefMode,
type_info: &std::rc::Rc<crate::TypeInfo>,
) -> Result<Option<T>, Error> {
match ref_mode {
RefMode::None => Ok(Some(C::read_with_type_info(
context,
RefMode::None,
type_info,
)?)),
RefMode::NullOnly => {
let ref_flag = context.reader.read_i8()?;
if ref_flag == RefFlag::Null as i8 {
return Ok(None);
}
Ok(Some(C::read_with_type_info(
context,
RefMode::None,
type_info,
)?))
}
RefMode::Tracking => {
let ref_flag = context.reader.read_i8()?;
if ref_flag == RefFlag::Null as i8 {
return Ok(None);
}
context.reader.move_back(1);
Ok(Some(C::read_with_type_info(
context,
RefMode::Tracking,
type_info,
)?))
}
}
}
#[inline(always)]
fn default_value(_: &mut ReadContext) -> Result<Option<T>, Error> {
Ok(None)
}
#[inline(always)]
fn write_type_info(context: &mut WriteContext) -> Result<(), Error> {
C::write_type_info(context)
}
#[inline(always)]
fn read_type_info(context: &mut ReadContext) -> Result<(), Error> {
C::read_type_info(context)
}
#[inline(always)]
fn static_type_id() -> TypeId {
C::static_type_id()
}
const IS_OPTIONAL: bool = true;
const IS_POLYMORPHIC: bool = C::IS_POLYMORPHIC;
const IS_SHARED_REF: bool = C::IS_SHARED_REF;
const IS_WRAPPER: bool = true;
const REQUIRES_SCOPED_ACCESS: bool = C::REQUIRES_SCOPED_ACCESS;
#[inline(always)]
fn is_none(value: &Option<T>) -> bool {
value.is_none()
}
#[inline(always)]
fn dynamic_type_id(value: &Option<T>) -> Result<Option<std::any::TypeId>, Error> {
match value {
Some(value) => C::dynamic_type_id(value),
None => Ok(None),
}
}
}
impl<T, C, const TRACK_REF: bool> Codec<Option<T>> for OptionCodec<T, C, TRACK_REF>
where
T: 'static,
C: Codec<T>,
{
#[inline(always)]
fn field_type(type_resolver: &TypeResolver) -> Result<FieldType, Error> {
let mut field_type = C::field_type(type_resolver)?;
field_type.nullable = true;
field_type.track_ref = TRACK_REF;
Ok(field_type)
}
#[inline(always)]
fn field_reserved_space() -> usize {
C::field_reserved_space() + 1
}
#[inline(always)]
fn write_field(value: &Option<T>, context: &mut WriteContext) -> Result<(), Error> {
Self::write_with_mode(
value,
context,
if TRACK_REF {
RefMode::Tracking
} else {
RefMode::NullOnly
},
codec_write_type_info::<T, C>(context),
true,
)
}
#[inline(always)]
fn read_field(context: &mut ReadContext) -> Result<Option<T>, Error> {
<Self as Serializer>::read(
context,
if TRACK_REF {
RefMode::Tracking
} else {
RefMode::NullOnly
},
codec_read_type_info_static::<T, C>(context),
)
}
#[inline(always)]
fn read_data_with_type(
context: &mut ReadContext,
remote_data_type: &FieldType,
) -> Result<Option<T>, Error> {
Ok(Some(C::read_data_with_type(context, remote_data_type)?))
}
#[inline(always)]
fn read_field_with_type(
context: &mut ReadContext,
remote_field_type: &FieldType,
) -> Result<Option<T>, Error> {
let ref_mode = field_ref_mode(remote_field_type);
if ref_mode != RefMode::None {
let ref_flag = context.reader.read_i8()?;
if ref_flag == RefFlag::Null as i8 {
return Ok(None);
}
context.reader.move_back(1);
}
Ok(Some(C::read_field_with_type(context, remote_field_type)?))
}
#[inline(always)]
fn read_compatible(
context: &mut ReadContext,
local_field_type: &FieldType,
remote_field_type: &FieldType,
) -> Result<Option<Option<T>>, Error> {
if field_types_compatible(local_field_type, remote_field_type)
|| local_field_type.compatible_shape_match(remote_field_type)
{
return Self::read_field_with_type(context, remote_field_type).map(Some);
}
let child_type_id = C::static_type_id() as u32;
if child_type_id != local_field_type.type_id
&& !same_numeric_family(child_type_id, local_field_type.type_id)
{
return Ok(None);
}
super::scalar_conversion::read_scalar_option_field::<T>(
context,
local_field_type,
remote_field_type,
)
}
#[inline(always)]
fn write_with_mode(
value: &Option<T>,
context: &mut WriteContext,
ref_mode: RefMode,
write_type_info: bool,
has_generics: bool,
) -> Result<(), Error> {
match ref_mode {
RefMode::None => {
let value = value.as_ref().ok_or_else(missing_option_value)?;
C::write_with_mode(value, context, RefMode::None, write_type_info, has_generics)
}
RefMode::NullOnly => {
if let Some(value) = value {
context.writer.write_i8(RefFlag::NotNullValue as i8);
C::write_with_mode(value, context, RefMode::None, write_type_info, has_generics)
} else {
context.writer.write_i8(RefFlag::Null as i8);
Ok(())
}
}
RefMode::Tracking => {
if let Some(value) = value {
C::write_with_mode(
value,
context,
RefMode::Tracking,
write_type_info,
has_generics,
)
} else {
context.writer.write_i8(RefFlag::Null as i8);
Ok(())
}
}
}
}
#[inline(always)]
fn write_with_type_info(
value: &Option<T>,
context: &mut WriteContext,
ref_mode: RefMode,
type_info: &Rc<crate::TypeInfo>,
has_generics: bool,
) -> Result<(), Error> {
match ref_mode {
RefMode::None => {
let value = value.as_ref().ok_or_else(missing_option_value)?;
<C as Codec<T>>::write_with_type_info(
value,
context,
RefMode::None,
type_info,
has_generics,
)
}
RefMode::NullOnly => {
if let Some(value) = value {
context.writer.write_i8(RefFlag::NotNullValue as i8);
<C as Codec<T>>::write_with_type_info(
value,
context,
RefMode::None,
type_info,
has_generics,
)
} else {
context.writer.write_i8(RefFlag::Null as i8);
Ok(())
}
}
RefMode::Tracking => {
if let Some(value) = value {
<C as Codec<T>>::write_with_type_info(
value,
context,
RefMode::Tracking,
type_info,
has_generics,
)
} else {
context.writer.write_i8(RefFlag::Null as i8);
Ok(())
}
}
}
}
#[inline(always)]
fn read_type_info_value(context: &mut ReadContext) -> Result<CodecReadType, Error> {
C::read_type_info_value(context)
}
}
macro_rules! signed_int_codec {
($name:ident, $ty:ty, $default_type:expr, $fixed_type:expr, $tagged_type:expr, $write_fixed:ident, $read_fixed:ident, $write_var:ident, $read_var:ident, $write_tagged:ident, $read_tagged:ident) => {
pub struct $name<const WIRE_TYPE_ID: u8, const NULLABLE: bool, const TRACK_REF: bool>;
impl<const WIRE_TYPE_ID: u8, const NULLABLE: bool, const TRACK_REF: bool> Serializer
for $name<WIRE_TYPE_ID, NULLABLE, TRACK_REF>
{
type Target = $ty;
#[inline(always)]
fn write_data(value: &$ty, context: &mut WriteContext) -> Result<(), Error> {
match WIRE_TYPE_ID as u32 {
x if x == $fixed_type => context.writer.$write_fixed(*value),
x if x == $tagged_type => context.writer.$write_tagged(*value),
_ => context.writer.$write_var(*value),
}
Ok(())
}
#[inline(always)]
fn read_data(context: &mut ReadContext) -> Result<$ty, Error> {
match WIRE_TYPE_ID as u32 {
x if x == $fixed_type => context.reader.$read_fixed(),
x if x == $tagged_type => context.reader.$read_tagged(),
_ => context.reader.$read_var(),
}
}
#[inline(always)]
fn read(
context: &mut ReadContext,
ref_mode: RefMode,
read_type_info: bool,
) -> Result<$ty, Error> {
if ref_mode != RefMode::None {
let ref_flag = context.reader.read_i8()?;
if ref_flag == RefFlag::Null as i8 {
return Self::default_value(context);
}
}
if read_type_info {
let remote = context.reader.read_var_u32()?;
if !same_numeric_family($default_type, remote) {
return Err(numeric_type_mismatch($default_type, remote));
}
return match remote {
x if x == $fixed_type => context.reader.$read_fixed(),
x if x == $tagged_type => context.reader.$read_tagged(),
_ => context.reader.$read_var(),
};
}
Self::read_data(context)
}
#[inline(always)]
fn read_with_type_info(
context: &mut ReadContext,
ref_mode: RefMode,
_type_info: &std::rc::Rc<crate::TypeInfo>,
) -> Result<$ty, Error> {
Self::read(context, ref_mode, false)
}
#[inline(always)]
fn default_value(_: &mut ReadContext) -> Result<$ty, Error> {
Ok(0 as $ty)
}
#[inline(always)]
fn write_type_info(context: &mut WriteContext) -> Result<(), Error> {
context.writer.write_var_u32(WIRE_TYPE_ID as u32);
Ok(())
}
#[inline(always)]
fn read_type_info(context: &mut ReadContext) -> Result<(), Error> {
let remote = context.reader.read_var_u32()?;
if !same_numeric_family($default_type, remote) {
return Err(numeric_type_mismatch($default_type, remote));
}
Ok(())
}
#[inline(always)]
fn static_type_id() -> TypeId {
TypeId::try_from(WIRE_TYPE_ID).unwrap_or(TypeId::UNKNOWN)
}
}
impl<const WIRE_TYPE_ID: u8, const NULLABLE: bool, const TRACK_REF: bool> Codec<$ty>
for $name<WIRE_TYPE_ID, NULLABLE, TRACK_REF>
{
#[inline(always)]
fn field_type(_: &TypeResolver) -> Result<FieldType, Error> {
Ok(FieldType::new_with_ref(
WIRE_TYPE_ID as u32,
NULLABLE,
TRACK_REF,
Vec::new(),
))
}
#[inline(always)]
fn field_reserved_space() -> usize {
std::mem::size_of::<$ty>() + 1
}
#[inline(always)]
fn write_field(value: &$ty, context: &mut WriteContext) -> Result<(), Error> {
if NULLABLE {
context.writer.write_i8(RefFlag::NotNullValue as i8);
}
Self::write_data(value, context)
}
#[inline(always)]
fn read_field(context: &mut ReadContext) -> Result<$ty, Error> {
if NULLABLE {
let ref_flag = context.reader.read_i8()?;
if ref_flag == RefFlag::Null as i8 {
return Self::default_value(context);
}
}
Self::read_data(context)
}
#[inline(always)]
fn read_data_with_type(
context: &mut ReadContext,
remote_data_type: &FieldType,
) -> Result<$ty, Error> {
match remote_data_type.type_id {
x if x == $fixed_type => context.reader.$read_fixed(),
x if x == $tagged_type => context.reader.$read_tagged(),
_ => context.reader.$read_var(),
}
}
#[inline(always)]
fn read_field_with_type(
context: &mut ReadContext,
remote_field_type: &FieldType,
) -> Result<$ty, Error> {
if field_ref_mode(remote_field_type) != RefMode::None {
let ref_flag = context.reader.read_i8()?;
if ref_flag == RefFlag::Null as i8 {
return Self::default_value(context);
}
}
Self::read_data_with_type(context, remote_field_type)
}
#[inline(always)]
fn write_with_mode(
value: &$ty,
context: &mut WriteContext,
ref_mode: RefMode,
write_type_info: bool,
_has_generics: bool,
) -> Result<(), Error> {
<Self as Serializer>::write(value, context, ref_mode, write_type_info)
}
#[inline(always)]
fn read_type_info_as_field_type(context: &mut ReadContext) -> Result<FieldType, Error> {
let remote = context.reader.read_var_u32()?;
if !same_numeric_family($default_type, remote) {
return Err(numeric_type_mismatch($default_type, remote));
}
Ok(FieldType::new(remote, false, Vec::new()))
}
}
};
}
#[cold]
#[inline(never)]
fn numeric_type_mismatch(expected: u32, actual: u32) -> Error {
Error::type_mismatch(expected, actual)
}
signed_int_codec!(
I32Codec,
i32,
type_id::VARINT32,
type_id::INT32,
UNKNOWN,
write_i32,
read_i32,
write_var_i32,
read_var_i32,
write_var_i32,
read_var_i32
);
signed_int_codec!(
I64Codec,
i64,
type_id::VARINT64,
type_id::INT64,
type_id::TAGGED_INT64,
write_i64,
read_i64,
write_var_i64,
read_var_i64,
write_tagged_i64,
read_tagged_i64
);
signed_int_codec!(
U32Codec,
u32,
type_id::VAR_UINT32,
type_id::UINT32,
UNKNOWN,
write_u32,
read_u32,
write_var_u32,
read_var_u32,
write_var_u32,
read_var_u32
);
signed_int_codec!(
U64Codec,
u64,
type_id::VAR_UINT64,
type_id::UINT64,
type_id::TAGGED_UINT64,
write_u64,
read_u64,
write_var_u64,
read_var_u64,
write_tagged_u64,
read_tagged_u64
);
pub struct VecCodec<
T,
C,
const STRUCTURAL_LIST: bool,
const DENSE_ARRAY: bool,
const NULLABLE: bool,
const TRACK_REF: bool,
>(PhantomData<(T, C)>);
#[cold]
#[inline(never)]
fn dense_array_requires_primitive<T: 'static>() -> Error {
Error::type_error(format!(
"explicit dense array encoding requires a canonical primitive target, got {}",
std::any::type_name::<T>(),
))
}
#[cold]
#[inline(never)]
fn list_type_mismatch(remote: u32) -> Error {
Error::type_mismatch(TypeId::LIST as u32, remote)
}
#[inline(always)]
fn selected_vec_type_id<T, C, const STRUCTURAL_LIST: bool, const DENSE_ARRAY: bool>(
) -> Result<Option<TypeId>, Error>
where
T: 'static,
C: Serializer<Target = T>,
{
if STRUCTURAL_LIST {
return Ok(None);
}
match primitive_list::array_type_id::<T, C>(DENSE_ARRAY) {
Some(type_id) => Ok(Some(type_id)),
None if DENSE_ARRAY => Err(dense_array_requires_primitive::<T>()),
None => Ok(None),
}
}
#[inline(always)]
fn write_vec_value_data<T, S, const STRUCTURAL_LIST: bool, const DENSE_ARRAY: bool>(
value: &Vec<T>,
context: &mut WriteContext,
) -> Result<(), Error>
where
T: 'static,
S: Serializer<Target = T>,
{
if let Some(type_id) = selected_vec_type_id::<T, S, STRUCTURAL_LIST, DENSE_ARRAY>()? {
return primitive_list::write_data::<T, S>(value, context, type_id);
}
super::collection::write_collection_value_data::<T, S, _, true, true>(value, context)
}
#[inline(always)]
fn read_vec_value_data<T, S, const STRUCTURAL_LIST: bool, const DENSE_ARRAY: bool>(
context: &mut ReadContext,
) -> Result<Vec<T>, Error>
where
T: 'static,
S: Serializer<Target = T>,
{
if let Some(type_id) = selected_vec_type_id::<T, S, STRUCTURAL_LIST, DENSE_ARRAY>()? {
return primitive_list::read_vec::<T, S>(context, type_id);
}
super::collection::read_collection_value_data::<Vec<T>, T, S, true, true>(context)
}
#[inline(always)]
fn write_vec_data<T, C, const STRUCTURAL_LIST: bool, const DENSE_ARRAY: bool>(
value: &Vec<T>,
context: &mut WriteContext,
has_generics: bool,
) -> Result<(), Error>
where
T: 'static,
C: Codec<T>,
{
if let Some(type_id) = selected_vec_type_id::<T, C, STRUCTURAL_LIST, DENSE_ARRAY>()? {
return primitive_list::write_data::<T, C>(value, context, type_id);
}
super::collection::write_collection_data::<T, C, _, true, true>(value, context, has_generics)
}
impl<
T,
S,
const STRUCTURAL_LIST: bool,
const DENSE_ARRAY: bool,
const NULLABLE: bool,
const TRACK_REF: bool,
> Serializer for VecCodec<T, S, STRUCTURAL_LIST, DENSE_ARRAY, NULLABLE, TRACK_REF>
where
T: 'static,
S: Serializer<Target = T>,
{
type Target = Vec<T>;
const READ_DATA_ALWAYS_ADVANCES: bool = true;
#[inline(always)]
fn reserved_space() -> usize {
if !STRUCTURAL_LIST && primitive_list::array_type_id::<T, S>(DENSE_ARRAY).is_some() {
primitive_list::reserved_space::<T>() + SIZE_OF_REF_AND_TYPE
} else {
std::mem::size_of::<u32>() + SIZE_OF_REF_AND_TYPE
}
}
#[inline(always)]
fn write_data(value: &Vec<T>, context: &mut WriteContext) -> Result<(), Error> {
write_vec_value_data::<T, S, STRUCTURAL_LIST, DENSE_ARRAY>(value, context)
}
#[inline(always)]
fn read_data(context: &mut ReadContext) -> Result<Vec<T>, Error> {
read_vec_value_data::<T, S, STRUCTURAL_LIST, DENSE_ARRAY>(context)
}
#[inline(always)]
fn write(
value: &Vec<T>,
context: &mut WriteContext,
ref_mode: RefMode,
write_type_info: bool,
) -> Result<(), Error> {
if ref_mode != RefMode::None {
context.writer.write_i8(RefFlag::NotNullValue as i8);
}
if write_type_info {
Self::write_type_info(context)?;
}
Self::write_data(value, context)
}
#[inline(always)]
fn read(
context: &mut ReadContext,
ref_mode: RefMode,
read_type_info: bool,
) -> Result<Vec<T>, Error> {
if ref_mode != RefMode::None && context.reader.read_i8()? == RefFlag::Null as i8 {
return Ok(Vec::new());
}
if read_type_info {
Self::read_type_info(context)?;
}
Self::read_data(context)
}
#[inline(always)]
fn read_with_type_info(
context: &mut ReadContext,
ref_mode: RefMode,
_type_info: &Rc<crate::TypeInfo>,
) -> Result<Vec<T>, Error> {
Self::read(context, ref_mode, false)
}
#[inline(always)]
fn default_value(_: &mut ReadContext) -> Result<Vec<T>, Error> {
Ok(Vec::new())
}
#[inline(always)]
fn write_type_info(context: &mut WriteContext) -> Result<(), Error> {
match selected_vec_type_id::<T, S, STRUCTURAL_LIST, DENSE_ARRAY>()? {
Some(type_id) => primitive_list::write_type_info(context, type_id),
None => {
context.writer.write_u8(TypeId::LIST as u8);
Ok(())
}
}
}
#[inline(always)]
fn read_type_info(context: &mut ReadContext) -> Result<(), Error> {
if let Some(type_id) = selected_vec_type_id::<T, S, STRUCTURAL_LIST, DENSE_ARRAY>()? {
return primitive_list::read_type_info(context, type_id);
}
let remote = context.reader.read_u8()? as u32;
if remote != TypeId::LIST as u32 {
return Err(list_type_mismatch(remote));
}
Ok(())
}
#[inline(always)]
fn static_type_id() -> TypeId {
if STRUCTURAL_LIST {
return TypeId::LIST;
}
match primitive_list::array_type_id::<T, S>(DENSE_ARRAY) {
Some(type_id) => type_id,
None => TypeId::LIST,
}
}
}
impl<
T,
C,
const STRUCTURAL_LIST: bool,
const DENSE_ARRAY: bool,
const NULLABLE: bool,
const TRACK_REF: bool,
> Codec<Vec<T>> for VecCodec<T, C, STRUCTURAL_LIST, DENSE_ARRAY, NULLABLE, TRACK_REF>
where
T: 'static,
C: Codec<T>,
{
#[inline(always)]
fn field_type(type_resolver: &TypeResolver) -> Result<FieldType, Error> {
if let Some(type_id) = selected_vec_type_id::<T, C, STRUCTURAL_LIST, DENSE_ARRAY>()? {
return Ok(FieldType::new_with_ref(
type_id as u32,
NULLABLE,
TRACK_REF,
Vec::new(),
));
}
let element_type = C::field_type(type_resolver)?;
Ok(FieldType::new_with_ref(
TypeId::LIST as u32,
NULLABLE,
TRACK_REF,
vec![element_type],
))
}
#[inline(always)]
fn write_field(value: &Vec<T>, context: &mut WriteContext) -> Result<(), Error> {
if NULLABLE || TRACK_REF {
context.writer.write_i8(RefFlag::NotNullValue as i8);
}
write_vec_data::<T, C, STRUCTURAL_LIST, DENSE_ARRAY>(value, context, true)
}
#[inline(always)]
fn read_field(context: &mut ReadContext) -> Result<Vec<T>, Error> {
if NULLABLE || TRACK_REF {
let ref_flag = context.reader.read_i8()?;
if ref_flag == RefFlag::Null as i8 {
return Ok(Vec::new());
}
}
<Self as Serializer>::read_data(context)
}
#[inline(always)]
fn read_compatible(
context: &mut ReadContext,
local_field_type: &FieldType,
remote_field_type: &FieldType,
) -> Result<Option<Vec<T>>, Error> {
if selected_vec_type_id::<T, C, STRUCTURAL_LIST, DENSE_ARRAY>()?.is_some() {
if field_types_compatible(local_field_type, remote_field_type) {
return Self::read_field_with_type(context, remote_field_type).map(Some);
}
return read_primitive_array_vec_mismatch::<T, C>(
context,
local_field_type,
remote_field_type,
);
}
if field_types_compatible(local_field_type, remote_field_type)
|| local_field_type.compatible_shape_match(remote_field_type)
{
return Self::read_field_with_type(context, remote_field_type).map(Some);
}
if local_field_type.type_id == remote_field_type.type_id
&& allows_missing_generics(local_field_type.type_id)
&& (local_field_type.generics.is_empty() || remote_field_type.generics.is_empty())
{
return Self::read_field_with_type(context, remote_field_type).map(Some);
}
read_vec_compatible_mismatch::<T, C>(context, local_field_type, remote_field_type)
}
fn read_data_with_type(
context: &mut ReadContext,
remote_field_type: &FieldType,
) -> Result<Vec<T>, Error> {
if let Some(type_id) = selected_vec_type_id::<T, C, STRUCTURAL_LIST, DENSE_ARRAY>()? {
if remote_field_type.type_id == TypeId::LIST as u32 {
return super::collection::read_list_as_primitive_vec::<T, C>(
context,
remote_field_type,
);
}
return primitive_list::read_vec::<T, C>(context, type_id);
}
super::collection::read_collection_data_with_type::<Vec<T>, T, C, true, true>(
context,
remote_field_type,
)
}
#[inline(always)]
fn read_field_with_type(
context: &mut ReadContext,
remote_field_type: &FieldType,
) -> Result<Vec<T>, Error> {
if field_ref_mode(remote_field_type) != RefMode::None {
let ref_flag = context.reader.read_i8()?;
if ref_flag == RefFlag::Null as i8 {
return Ok(Vec::new());
}
}
Self::read_data_with_type(context, remote_field_type)
}
#[inline(always)]
fn write_with_mode(
value: &Vec<T>,
context: &mut WriteContext,
ref_mode: RefMode,
write_type_info: bool,
has_generics: bool,
) -> Result<(), Error> {
if ref_mode != RefMode::None {
context.writer.write_i8(RefFlag::NotNullValue as i8);
}
if write_type_info {
Self::write_type_info(context)?;
}
write_vec_data::<T, C, STRUCTURAL_LIST, DENSE_ARRAY>(value, context, has_generics)
}
}
#[inline(always)]
fn any_field_type<const NULLABLE: bool, const TRACK_REF: bool>() -> FieldType {
FieldType::new_with_ref(TypeId::UNKNOWN as u32, NULLABLE, TRACK_REF, Vec::new())
}
#[inline(always)]
fn any_ref_mode<const NULLABLE: bool, const TRACK_REF: bool>() -> RefMode {
if TRACK_REF {
RefMode::Tracking
} else if NULLABLE {
RefMode::NullOnly
} else {
RefMode::None
}
}
macro_rules! any_codec {
($name:ident, $ty:ty) => {
pub struct $name<const NULLABLE: bool, const TRACK_REF: bool>;
impl<const NULLABLE: bool, const TRACK_REF: bool> Serializer
for $name<NULLABLE, TRACK_REF>
{
type Target = $ty;
#[inline(always)]
fn write_data(value: &$ty, context: &mut WriteContext) -> Result<(), Error> {
<$ty as Serializer>::write_data(value, context)
}
#[inline(always)]
fn read_data(context: &mut ReadContext) -> Result<$ty, Error> {
<$ty as Serializer>::read_data(context)
}
#[inline(always)]
fn write(
value: &$ty,
context: &mut WriteContext,
ref_mode: RefMode,
write_type_info: bool,
) -> Result<(), Error> {
<$ty as Serializer>::write(value, context, ref_mode, write_type_info)
}
#[inline(always)]
fn write_type_info_value(
context: &mut WriteContext,
target_type_id: std::any::TypeId,
) -> Result<Rc<crate::TypeInfo>, Error> {
<$ty as Serializer>::write_type_info_value(context, target_type_id)
}
#[inline(always)]
fn write_with_type_info(
value: &$ty,
context: &mut WriteContext,
ref_mode: RefMode,
type_info: &Rc<crate::TypeInfo>,
) -> Result<(), Error> {
<$ty as Serializer>::write_with_type_info(value, context, ref_mode, type_info)
}
#[inline(always)]
fn read(
context: &mut ReadContext,
ref_mode: RefMode,
read_type_info: bool,
) -> Result<$ty, Error> {
<$ty as Serializer>::read(context, ref_mode, read_type_info)
}
#[inline(always)]
fn read_with_type_info(
context: &mut ReadContext,
ref_mode: RefMode,
type_info: &Rc<crate::TypeInfo>,
) -> Result<$ty, Error> {
<$ty as Serializer>::read_with_type_info(context, ref_mode, type_info)
}
#[inline(always)]
fn default_value(context: &mut ReadContext) -> Result<$ty, Error> {
<$ty as Serializer>::default_value(context)
}
#[inline(always)]
fn write_type_info(context: &mut WriteContext) -> Result<(), Error> {
<$ty as Serializer>::write_type_info(context)
}
#[inline(always)]
fn read_type_info(context: &mut ReadContext) -> Result<(), Error> {
<$ty as Serializer>::read_type_info(context)
}
#[inline(always)]
fn static_type_id() -> TypeId {
TypeId::UNKNOWN
}
const IS_OPTIONAL: bool = <$ty as Serializer>::IS_OPTIONAL;
const IS_POLYMORPHIC: bool = true;
const IS_SHARED_REF: bool = <$ty as Serializer>::IS_SHARED_REF;
const IS_WRAPPER: bool = <$ty as Serializer>::IS_WRAPPER;
const REQUIRES_SCOPED_ACCESS: bool = <$ty as Serializer>::REQUIRES_SCOPED_ACCESS;
#[inline(always)]
fn dynamic_type_id(value: &$ty) -> Result<Option<std::any::TypeId>, Error> {
<$ty as Serializer>::dynamic_type_id(value)
}
#[inline(always)]
fn reserved_space() -> usize {
<$ty as Serializer>::reserved_space()
}
}
impl<const NULLABLE: bool, const TRACK_REF: bool> Codec<$ty>
for $name<NULLABLE, TRACK_REF>
{
#[inline(always)]
fn field_type(_: &TypeResolver) -> Result<FieldType, Error> {
Ok(any_field_type::<NULLABLE, TRACK_REF>())
}
#[inline(always)]
fn field_reserved_space() -> usize {
<$ty as Serializer>::reserved_space() + SIZE_OF_REF_AND_TYPE
}
#[inline(always)]
fn write_field(value: &$ty, context: &mut WriteContext) -> Result<(), Error> {
<$ty as Serializer>::write(
value,
context,
any_ref_mode::<NULLABLE, TRACK_REF>(),
field_write_type_info::<$ty>(context),
)
}
#[inline(always)]
fn read_field(context: &mut ReadContext) -> Result<$ty, Error> {
<$ty as Serializer>::read(
context,
any_ref_mode::<NULLABLE, TRACK_REF>(),
codec_read_type_info_static::<$ty, Self>(context),
)
}
#[inline(always)]
fn read_field_with_type(
context: &mut ReadContext,
remote_field_type: &FieldType,
) -> Result<$ty, Error> {
<$ty as Serializer>::read(
context,
field_ref_mode(remote_field_type),
codec_read_type_info::<$ty, Self>(context, remote_field_type),
)
}
#[inline(always)]
fn write_with_mode(
value: &$ty,
context: &mut WriteContext,
ref_mode: RefMode,
write_type_info: bool,
_has_generics: bool,
) -> Result<(), Error> {
<$ty as Serializer>::write(value, context, ref_mode, write_type_info)
}
#[inline(always)]
fn write_with_type_info(
value: &$ty,
context: &mut WriteContext,
ref_mode: RefMode,
type_info: &Rc<crate::TypeInfo>,
_has_generics: bool,
) -> Result<(), Error> {
<$ty as Serializer>::write_with_type_info(value, context, ref_mode, type_info)
}
#[inline(always)]
fn read_type_info_value(context: &mut ReadContext) -> Result<CodecReadType, Error> {
context.read_any_type_info().map(CodecReadType::TypeInfo)
}
}
};
}
any_codec!(AnyBoxCodec, Box<dyn Any>);
any_codec!(AnyRcCodec, Rc<dyn Any>);
any_codec!(AnyArcCodec, Arc<dyn Any + Send + Sync>);
#[cfg(test)]
mod tests {
use super::{
compatible_field_pair, field_types_compatible, BoxCodec, Codec, CodecReadType, OptionCodec,
SerializerCodec, VecCodec,
};
use crate::buffer::Reader;
use crate::config::Config;
use crate::context::{ReadContext, WriteContext};
use crate::error::Error;
use crate::meta::FieldType;
use crate::resolver::{RefMode, TypeResolver};
use crate::serializer::collection::{DECL_ELEMENT_TYPE, IS_SAME_TYPE, TRACKING_REF};
use crate::serializer::Serializer;
use crate::type_id::{self, TypeId, SIZE_OF_REF_AND_TYPE};
use std::cell::{Cell, RefCell};
use std::rc::Rc;
#[derive(Debug, PartialEq)]
struct MetadataProbe(u8);
struct MetadataProbeCodec;
struct PropertySerializer;
thread_local! {
static EXPECTED_FIELD: Cell<*const FieldType> =
const { Cell::new(std::ptr::null()) };
static PROBE_FIELD_READS: Cell<usize> = const { Cell::new(0) };
static PROBE_TYPE_INFO: RefCell<Option<Rc<crate::TypeInfo>>> =
const { RefCell::new(None) };
static PROBE_INFO_BASE_REFS: Cell<usize> = const { Cell::new(0) };
static PROBE_INFO_READS: Cell<usize> = const { Cell::new(0) };
}
impl Serializer for MetadataProbeCodec {
type Target = MetadataProbe;
fn write_data(value: &MetadataProbe, context: &mut WriteContext) -> Result<(), Error> {
context.writer.write_u8(value.0);
Ok(())
}
fn read_data(context: &mut ReadContext) -> Result<MetadataProbe, Error> {
context.reader.read_u8().map(MetadataProbe)
}
fn read_with_type_info(
context: &mut ReadContext,
ref_mode: RefMode,
type_info: &Rc<crate::TypeInfo>,
) -> Result<MetadataProbe, Error> {
assert_eq!(ref_mode, RefMode::Tracking);
PROBE_TYPE_INFO.with(|canonical| {
let canonical = canonical.borrow();
let canonical = canonical.as_ref().expect("canonical TypeInfo");
assert!(Rc::ptr_eq(type_info, canonical));
PROBE_INFO_BASE_REFS.with(|base| {
assert_eq!(Rc::strong_count(type_info), base.get() + 1);
});
});
PROBE_INFO_READS.with(|reads| reads.set(reads.get() + 1));
Self::read_data(context)
}
fn default_value(_context: &mut ReadContext) -> Result<MetadataProbe, Error> {
Ok(MetadataProbe(0))
}
fn write_type_info(context: &mut WriteContext) -> Result<(), Error> {
context.writer.write_u8(TypeId::INT8 as u8);
Ok(())
}
fn read_type_info(context: &mut ReadContext) -> Result<(), Error> {
let _ = context.reader.read_u8()?;
Ok(())
}
fn static_type_id() -> TypeId {
TypeId::INT8
}
const IS_SHARED_REF: bool = true;
}
impl Codec<MetadataProbe> for MetadataProbeCodec {
fn field_type(_type_resolver: &TypeResolver) -> Result<FieldType, Error> {
Ok(FieldType::new_with_ref(
type_id::INT8,
false,
true,
Vec::new(),
))
}
fn write_field(value: &MetadataProbe, context: &mut WriteContext) -> Result<(), Error> {
Self::write_data(value, context)
}
fn read_field(context: &mut ReadContext) -> Result<MetadataProbe, Error> {
Self::read_data(context)
}
fn read_data_with_type(
context: &mut ReadContext,
remote_data_type: &FieldType,
) -> Result<MetadataProbe, Error> {
EXPECTED_FIELD.with(|expected| {
assert!(std::ptr::eq(remote_data_type, expected.get()));
});
PROBE_FIELD_READS.with(|reads| reads.set(reads.get() + 1));
Self::read_data(context)
}
fn read_field_with_type(
context: &mut ReadContext,
remote_field_type: &FieldType,
) -> Result<MetadataProbe, Error> {
Self::read_data_with_type(context, remote_field_type)
}
fn write_with_mode(
value: &MetadataProbe,
context: &mut WriteContext,
_ref_mode: RefMode,
_write_type_info: bool,
_has_generics: bool,
) -> Result<(), Error> {
Self::write_data(value, context)
}
fn read_type_info_value(_context: &mut ReadContext) -> Result<CodecReadType, Error> {
PROBE_TYPE_INFO.with(|canonical| {
let canonical = canonical.borrow();
Ok(CodecReadType::TypeInfo(Rc::clone(
canonical.as_ref().expect("canonical TypeInfo"),
)))
})
}
}
impl Serializer for PropertySerializer {
type Target = MetadataProbe;
const IS_OPTIONAL: bool = true;
const IS_POLYMORPHIC: bool = true;
const IS_SHARED_REF: bool = false;
const IS_WRAPPER: bool = true;
const REQUIRES_SCOPED_ACCESS: bool = true;
fn write_data(value: &MetadataProbe, context: &mut WriteContext) -> Result<(), Error> {
MetadataProbeCodec::write_data(value, context)
}
fn read_data(context: &mut ReadContext) -> Result<MetadataProbe, Error> {
MetadataProbeCodec::read_data(context)
}
}
const _: () = {
type Leaf = SerializerCodec<PropertySerializer, false, false>;
assert!(Leaf::IS_OPTIONAL == PropertySerializer::IS_OPTIONAL);
assert!(Leaf::IS_POLYMORPHIC == PropertySerializer::IS_POLYMORPHIC);
assert!(Leaf::IS_SHARED_REF == PropertySerializer::IS_SHARED_REF);
assert!(Leaf::IS_WRAPPER == PropertySerializer::IS_WRAPPER);
assert!(Leaf::REQUIRES_SCOPED_ACCESS == PropertySerializer::REQUIRES_SCOPED_ACCESS);
};
type ProbeVecCodec = VecCodec<MetadataProbe, MetadataProbeCodec, true, false, false, false>;
#[test]
fn field_capacity_belongs_to_codec() {
type Leaf = SerializerCodec<MetadataProbeCodec, false, false>;
type Optional = OptionCodec<MetadataProbe, Leaf, false>;
type Boxed = BoxCodec<MetadataProbe, Leaf, false, false>;
let value_space = <MetadataProbeCodec as Serializer>::reserved_space();
assert_eq!(<Leaf as Serializer>::reserved_space(), value_space);
assert_eq!(
<Leaf as Codec<MetadataProbe>>::field_reserved_space(),
value_space + SIZE_OF_REF_AND_TYPE
);
assert_eq!(<Optional as Serializer>::reserved_space(), value_space + 1);
assert_eq!(
<Optional as Codec<Option<MetadataProbe>>>::field_reserved_space(),
value_space + SIZE_OF_REF_AND_TYPE + 1
);
assert_eq!(<Boxed as Serializer>::reserved_space(), value_space);
assert_eq!(
<Boxed as Codec<Box<MetadataProbe>>>::field_reserved_space(),
value_space + SIZE_OF_REF_AND_TYPE
);
}
fn probe_context(bytes: &[u8]) -> ReadContext<'_> {
let config = Config::default();
let graph_memory = config.max_graph_memory_bytes;
let mut context = ReadContext::new(TypeResolver::default(), config);
context.remaining_graph_memory_bytes = graph_memory;
context.attach_reader(Reader::new(bytes));
context
}
#[test]
fn byte_sequence_compatibility() {
let bytes = FieldType::new(type_id::BINARY, false, vec![]);
let uint8_array = FieldType::new(type_id::UINT8_ARRAY, false, vec![]);
let int8_array = FieldType::new(type_id::INT8_ARRAY, false, vec![]);
assert!(!field_types_compatible(&bytes, &uint8_array));
assert!(!field_types_compatible(&uint8_array, &bytes));
assert!(compatible_field_pair(&bytes, &uint8_array));
assert!(compatible_field_pair(&uint8_array, &bytes));
assert!(!field_types_compatible(&bytes, &int8_array));
assert!(!field_types_compatible(&int8_array, &bytes));
assert!(!compatible_field_pair(&bytes, &int8_array));
assert!(!compatible_field_pair(&int8_array, &bytes));
}
#[test]
fn scalar_ref_tracking_rules() {
let bool_value = FieldType::new(type_id::BOOL, false, vec![]);
let ref_bool = FieldType::new_with_ref(type_id::BOOL, false, true, vec![]);
assert!(!field_types_compatible(&bool_value, &ref_bool));
assert!(!field_types_compatible(&ref_bool, &bool_value));
assert!(field_types_compatible(&ref_bool, &ref_bool));
let nullable_ref_bool = FieldType::new_with_ref(type_id::BOOL, true, true, vec![]);
assert!(field_types_compatible(
&nullable_ref_bool,
&nullable_ref_bool
));
assert!(!field_types_compatible(&ref_bool, &nullable_ref_bool));
assert!(!field_types_compatible(&nullable_ref_bool, &ref_bool));
let fixed_i32 = FieldType::new(type_id::INT32, false, vec![]);
let var_i32 = FieldType::new(type_id::VARINT32, false, vec![]);
assert!(!field_types_compatible(&fixed_i32, &var_i32));
assert!(compatible_field_pair(&fixed_i32, &var_i32));
let ref_fixed_i32 = FieldType::new_with_ref(type_id::INT32, false, true, vec![]);
let ref_var_i32 = FieldType::new_with_ref(type_id::VARINT32, false, true, vec![]);
assert!(!field_types_compatible(&ref_fixed_i32, &ref_var_i32));
}
#[test]
fn compatible_field_pair_rules() {
let int8 = FieldType::new(type_id::INT8, false, vec![]);
let int16 = FieldType::new(type_id::INT16, false, vec![]);
assert!(compatible_field_pair(&int16, &int8));
let ref_int16 = FieldType::new_with_ref(type_id::INT16, false, true, vec![]);
assert!(!compatible_field_pair(&ref_int16, &int8));
let list_i8 = FieldType::new(type_id::LIST, false, vec![int8]);
let list_i16 = FieldType::new(type_id::LIST, false, vec![int16]);
assert!(!compatible_field_pair(&list_i16, &list_i8));
let list_fixed_i32 = FieldType::new(
type_id::LIST,
false,
vec![FieldType::new(type_id::INT32, false, vec![])],
);
let list_var_i32 = FieldType::new(
type_id::LIST,
false,
vec![FieldType::new(type_id::VARINT32, false, vec![])],
);
assert!(!field_types_compatible(&list_fixed_i32, &list_var_i32));
assert!(!compatible_field_pair(&list_fixed_i32, &list_var_i32));
let list_nullable_i32 = FieldType::new(
type_id::LIST,
false,
vec![FieldType::new(type_id::INT32, true, vec![])],
);
assert!(!field_types_compatible(&list_fixed_i32, &list_nullable_i32));
assert!(compatible_field_pair(&list_fixed_i32, &list_nullable_i32));
let int32_array = FieldType::new(type_id::INT32_ARRAY, false, vec![]);
let list_i32 = FieldType::new(
type_id::LIST,
false,
vec![FieldType::new(type_id::INT32, false, vec![])],
);
assert!(compatible_field_pair(&list_i32, &int32_array));
assert!(compatible_field_pair(&list_nullable_i32, &int32_array));
assert!(compatible_field_pair(&int32_array, &list_i32));
}
#[test]
fn vec_metadata_is_borrowed() {
let remote_field_type = FieldType::new(
type_id::LIST,
false,
vec![FieldType::new_with_ref(
type_id::INT8,
false,
true,
Vec::new(),
)],
);
EXPECTED_FIELD.with(|expected| {
expected.set(&remote_field_type.generics[0]);
});
PROBE_FIELD_READS.with(|reads| reads.set(0));
let declared = [3, IS_SAME_TYPE | DECL_ELEMENT_TYPE | TRACKING_REF, 4, 5, 6];
let mut context = probe_context(&declared);
let values = <ProbeVecCodec as Codec<Vec<MetadataProbe>>>::read_data_with_type(
&mut context,
&remote_field_type,
)
.unwrap();
assert_eq!(
values,
vec![MetadataProbe(4), MetadataProbe(5), MetadataProbe(6)]
);
PROBE_FIELD_READS.with(|reads| assert_eq!(reads.get(), 3));
EXPECTED_FIELD.with(|expected| expected.set(std::ptr::null()));
let indexed = [3, IS_SAME_TYPE | TRACKING_REF, TypeId::INT8 as u8, 7, 8, 9];
let mut context = probe_context(&indexed);
let type_info = context
.get_type_resolver()
.get_type_info_by_id(TypeId::INT8 as u32)
.expect("INT8 TypeInfo");
PROBE_TYPE_INFO.with(|canonical| {
*canonical.borrow_mut() = Some(type_info);
});
PROBE_TYPE_INFO.with(|canonical| {
let canonical = canonical.borrow();
PROBE_INFO_BASE_REFS
.with(|base| base.set(Rc::strong_count(canonical.as_ref().unwrap())));
});
PROBE_INFO_READS.with(|reads| reads.set(0));
let values = <ProbeVecCodec as Serializer>::read_data(&mut context).unwrap();
assert_eq!(
values,
vec![MetadataProbe(7), MetadataProbe(8), MetadataProbe(9)]
);
PROBE_INFO_READS.with(|reads| assert_eq!(reads.get(), 3));
PROBE_TYPE_INFO.with(|canonical| {
let canonical = canonical.borrow();
PROBE_INFO_BASE_REFS.with(|base| {
assert_eq!(Rc::strong_count(canonical.as_ref().unwrap()), base.get());
});
});
PROBE_TYPE_INFO.with(|canonical| *canonical.borrow_mut() = None);
}
}