diff --git a/src/serde/ISerialize.cs b/src/serde/ISerialize.cs index 369526bf..569fb217 100644 --- a/src/serde/ISerialize.cs +++ b/src/serde/ISerialize.cs @@ -1,6 +1,4 @@ using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; namespace Serde; @@ -72,222 +70,4 @@ public static ISerialize GetSerialize() => TProvider.Instance; } -public interface ISerializer -{ - void WriteBool(bool b); - void WriteChar(char c); - void WriteU8(byte b); - void WriteU16(ushort u16); - void WriteU32(uint u32); - void WriteU64(ulong u64); - void WriteI8(sbyte b); - void WriteI16(short i16); - void WriteI32(int i32); - void WriteI64(long i64); - void WriteF32(float f); - void WriteF64(double d); - void WriteDecimal(decimal d); - void WriteString(string s); - void WriteNull(); - void WriteDateTime(DateTime dt); - void WriteDateTimeOffset(DateTimeOffset dt); - void WriteBytes(ReadOnlyMemory bytes); - void WriteDateOnly(DateOnly d) - { - // Default implementation: serialize as ISO 8601 date string - WriteString(d.ToString("yyyy-MM-dd")); - } - void WriteTimeOnly(TimeOnly t) - { - // Default implementation: serialize as ISO 8601 time string - WriteString(t.ToString("HH:mm:ss")); - } - - /// - /// Write a collection type -- either a list or a dictionary. - /// - /// - /// The type information. The must be either or . - /// - /// - /// The size of the type. This is the number of elements. This parameter may be null if the size - /// is not known at the call site, but certain formats may not support types of unknown size and - /// throw . - /// - /// - /// An that can be used to serialize the type. After this method - /// is called, the retuned should be used to serialize the type. - /// The method should be called when the type is fully - /// serialized. The parent should not be used after this method is - /// called, until the method is called. Before the method is called, all operations on the parent have undefined behavior. - /// - ITypeSerializer WriteCollection(ISerdeInfo info, int? count); - - /// - /// Write a non-collection, non-primitive type. This could be a custom type, an enum, a union, a - /// nullable type, etc. The full set of options corresponds to the options represented by . - /// - /// - /// An that can be used to serialize the type. After this method - /// is called, the retuned should be used to serialize the type. - /// The method should be called when the type is fully - /// serialized. The parent should not be used after this method is - /// called, until the method is called. Before the method is called, all operations on the parent have undefined behavior. - /// - ITypeSerializer WriteType(ISerdeInfo info); -} - -public static class ISerializeExt -{ - public static void WriteValue(this ISerializer serializer, T value) - where TProvider : ISerializeProvider - { - var ser = TProvider.Instance; - ser.Serialize(value, serializer); - } - public static void WriteValue(this ISerializer serializer, T value) - where T : ISerializeProvider - => serializer.WriteValue(value); -} - - -/// -/// This interface is used to serialize non-primitive types. All non-primitive types are -/// aggregates of other types. For custom types (structs and classes) these are member fields. -/// For collections these are the elements in collections. For enums, this is the underlying value -/// inside the enum. -/// -/// The operations on are used to serialize -/// each of the contained types. The method should be called when the -/// type is fully serialized. -/// -public interface ITypeSerializer -{ - void WriteBool(ISerdeInfo typeInfo, int index, bool b); - void WriteChar(ISerdeInfo typeInfo, int index, char c); - void WriteU8(ISerdeInfo typeInfo, int index, byte b); - void WriteU16(ISerdeInfo typeInfo, int index, ushort u16); - void WriteU32(ISerdeInfo typeInfo, int index, uint u32); - void WriteU64(ISerdeInfo typeInfo, int index, ulong u64); - void WriteI8(ISerdeInfo typeInfo, int index, sbyte b); - void WriteI16(ISerdeInfo typeInfo, int index, short i16); - void WriteI32(ISerdeInfo typeInfo, int index, int i32); - void WriteI64(ISerdeInfo typeInfo, int index, long i64); - void WriteF32(ISerdeInfo typeInfo, int index, float f); - void WriteF64(ISerdeInfo typeInfo, int index, double d); - void WriteDecimal(ISerdeInfo typeInfo, int index, decimal d); - void WriteString(ISerdeInfo typeInfo, int index, string s); - void WriteNull(ISerdeInfo typeInfo, int index); - void WriteDateTime(ISerdeInfo typeInfo, int index, DateTime dt); - void WriteDateTimeOffset(ISerdeInfo typeInfo, int index, DateTimeOffset dt); - void WriteDateOnly(ISerdeInfo typeInfo, int index, DateOnly d) - { - // Default implementation: serialize as ISO 8601 date string - WriteString(typeInfo, index, d.ToString("yyyy-MM-dd")); - } - void WriteTimeOnly(ISerdeInfo typeInfo, int index, TimeOnly t) - { - // Default implementation: serialize as ISO 8601 time string - WriteString(typeInfo, index, t.ToString("HH:mm:ss")); - } - void WriteBytes(ISerdeInfo typeInfo, int index, ReadOnlyMemory bytes); - /// - /// Write an arbitrary value with custom serialization. For reference types this method may be - /// used directly. For value types, should be used instead. - /// - void WriteValue(ISerdeInfo typeInfo, int index, T value, ISerialize serialize) - where T : class?; - void SkipValue(ISerdeInfo typeInfo, int index) { } - void End(ISerdeInfo info); -} - -public static class ITypeSerializerExt -{ - public static void WriteValue( - this ITypeSerializer serializeType, - ISerdeInfo typeInfo, - int index, - T value) - where T : class? - where TProvider : ISerializeProvider - => serializeType.WriteValue(typeInfo, index, value, TProvider.Instance); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteStringIfNotNull( - this ITypeSerializer serializeType, - ISerdeInfo typeInfo, - int index, - string? value) - { - if (value is null) - { - serializeType.SkipValue(typeInfo, index); - } - else - { - serializeType.WriteString(typeInfo, index, value); - } - } - - public static void WriteValueIfNotNull( - this ITypeSerializer serializeType, - ISerdeInfo typeInfo, - int index, - T value, - ISerialize proxy) - where T : class? - { - if (value is null) - { - serializeType.SkipValue(typeInfo, index); - } - else - { - serializeType.WriteValue(typeInfo, index, value, proxy); - } - } - - public static void WriteValueIfNotNull( - this ITypeSerializer serializeType, - ISerdeInfo typeInfo, - int index, - T value) - where T : class? - where TProvider : ISerializeProvider - => serializeType.WriteValueIfNotNull(typeInfo, index, value, TProvider.Instance); - - public static void WriteBoxedValue( - this ITypeSerializer serializeType, - ISerdeInfo serdeInfo, - int index, - T value) - where TProvider : ISerializeProvider - { - serializeType.WriteValue(serdeInfo, index, value, BoxProxy.Ser.Instance); - } - - public static void WriteBoxedValueIfNotNull( - this ITypeSerializer serializeType, - ISerdeInfo typeInfo, - int index, - T value) - where TProvider : ISerializeProvider - { - if (value is null) - { - serializeType.SkipValue(typeInfo, index); - } - else - { - serializeType.WriteBoxedValue(typeInfo, index, value); - } - } -} diff --git a/src/serde/ISerializer.cs b/src/serde/ISerializer.cs new file mode 100644 index 00000000..711e4f96 --- /dev/null +++ b/src/serde/ISerializer.cs @@ -0,0 +1,224 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Serde; + +public interface ISerializer +{ + void WriteBool(bool b); + void WriteChar(char c); + void WriteU8(byte b); + void WriteU16(ushort u16); + void WriteU32(uint u32); + void WriteU64(ulong u64); + void WriteI8(sbyte b); + void WriteI16(short i16); + void WriteI32(int i32); + void WriteI64(long i64); + void WriteF32(float f); + void WriteF64(double d); + void WriteDecimal(decimal d); + void WriteString(string s); + void WriteNull(); + void WriteDateTime(DateTime dt); + void WriteDateTimeOffset(DateTimeOffset dt); + void WriteBytes(ReadOnlyMemory bytes); + void WriteDateOnly(DateOnly d) + { + // Default implementation: serialize as ISO 8601 date string + WriteString(d.ToString("yyyy-MM-dd")); + } + void WriteTimeOnly(TimeOnly t) + { + // Default implementation: serialize as ISO 8601 time string + WriteString(t.ToString("HH:mm:ss")); + } + + /// + /// Write a collection type -- either a list or a dictionary. + /// + /// + /// The type information. The must be either or . + /// + /// + /// The size of the type. This is the number of elements. This parameter may be null if the size + /// is not known at the call site, but certain formats may not support types of unknown size and + /// throw . + /// + /// + /// An that can be used to serialize the type. After this method + /// is called, the retuned should be used to serialize the type. + /// The method should be called when the type is fully + /// serialized. The parent should not be used after this method is + /// called, until the method is called. Before the method is called, all operations on the parent have undefined behavior. + /// + ITypeSerializer WriteCollection(ISerdeInfo info, int? count); + + /// + /// Write a non-collection, non-primitive type. This could be a custom type, an enum, a union, a + /// nullable type, etc. The full set of options corresponds to the options represented by . + /// + /// + /// An that can be used to serialize the type. After this method + /// is called, the retuned should be used to serialize the type. + /// The method should be called when the type is fully + /// serialized. The parent should not be used after this method is + /// called, until the method is called. Before the method is called, all operations on the parent have undefined behavior. + /// + ITypeSerializer WriteType(ISerdeInfo info); +} + +public static class ISerializeExt +{ + public static void WriteValue(this ISerializer serializer, T value) + where TProvider : ISerializeProvider + { + var ser = TProvider.Instance; + ser.Serialize(value, serializer); + } + public static void WriteValue(this ISerializer serializer, T value) + where T : ISerializeProvider + => serializer.WriteValue(value); +} + + +/// +/// This interface is used to serialize non-primitive types. All non-primitive types are +/// aggregates of other types. For custom types (structs and classes) these are member fields. +/// For collections these are the elements in collections. For enums, this is the underlying value +/// inside the enum. +/// +/// The operations on are used to serialize +/// each of the contained types. The method should be called when the +/// type is fully serialized. +/// +public interface ITypeSerializer +{ + void WriteBool(ISerdeInfo typeInfo, int index, bool b); + void WriteChar(ISerdeInfo typeInfo, int index, char c); + void WriteU8(ISerdeInfo typeInfo, int index, byte b); + void WriteU16(ISerdeInfo typeInfo, int index, ushort u16); + void WriteU32(ISerdeInfo typeInfo, int index, uint u32); + void WriteU64(ISerdeInfo typeInfo, int index, ulong u64); + void WriteI8(ISerdeInfo typeInfo, int index, sbyte b); + void WriteI16(ISerdeInfo typeInfo, int index, short i16); + void WriteI32(ISerdeInfo typeInfo, int index, int i32); + void WriteI64(ISerdeInfo typeInfo, int index, long i64); + void WriteF32(ISerdeInfo typeInfo, int index, float f); + void WriteF64(ISerdeInfo typeInfo, int index, double d); + void WriteDecimal(ISerdeInfo typeInfo, int index, decimal d); + void WriteString(ISerdeInfo typeInfo, int index, string s); + void WriteNull(ISerdeInfo typeInfo, int index); + void WriteDateTime(ISerdeInfo typeInfo, int index, DateTime dt); + void WriteDateTimeOffset(ISerdeInfo typeInfo, int index, DateTimeOffset dt); + void WriteDateOnly(ISerdeInfo typeInfo, int index, DateOnly d) + { + // Default implementation: serialize as ISO 8601 date string + WriteString(typeInfo, index, d.ToString("yyyy-MM-dd")); + } + void WriteTimeOnly(ISerdeInfo typeInfo, int index, TimeOnly t) + { + // Default implementation: serialize as ISO 8601 time string + WriteString(typeInfo, index, t.ToString("HH:mm:ss")); + } + void WriteBytes(ISerdeInfo typeInfo, int index, ReadOnlyMemory bytes); + + /// + /// Write an arbitrary value with custom serialization. For reference types this method may be + /// used directly. For value types, should be used instead. + /// + void WriteValue(ISerdeInfo typeInfo, int index, T value, ISerialize serialize) + where T : class?; + void SkipValue(ISerdeInfo typeInfo, int index) { } + void End(ISerdeInfo info); +} + +public static class ITypeSerializerExt +{ + public static void WriteValue( + this ITypeSerializer serializeType, + ISerdeInfo typeInfo, + int index, + T value) + where T : class? + where TProvider : ISerializeProvider + => serializeType.WriteValue(typeInfo, index, value, TProvider.Instance); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteStringIfNotNull( + this ITypeSerializer serializeType, + ISerdeInfo typeInfo, + int index, + string? value) + { + if (value is null) + { + serializeType.SkipValue(typeInfo, index); + } + else + { + serializeType.WriteString(typeInfo, index, value); + } + } + + public static void WriteValueIfNotNull( + this ITypeSerializer serializeType, + ISerdeInfo typeInfo, + int index, + T value, + ISerialize proxy) + where T : class? + { + if (value is null) + { + serializeType.SkipValue(typeInfo, index); + } + else + { + serializeType.WriteValue(typeInfo, index, value, proxy); + } + } + + public static void WriteValueIfNotNull( + this ITypeSerializer serializeType, + ISerdeInfo typeInfo, + int index, + T value) + where T : class? + where TProvider : ISerializeProvider + => serializeType.WriteValueIfNotNull(typeInfo, index, value, TProvider.Instance); + + public static void WriteBoxedValue( + this ITypeSerializer serializeType, + ISerdeInfo serdeInfo, + int index, + T value) + where TProvider : ISerializeProvider + { + serializeType.WriteValue(serdeInfo, index, value, BoxProxy.Ser.Instance); + } + + public static void WriteBoxedValueIfNotNull( + this ITypeSerializer serializeType, + ISerdeInfo typeInfo, + int index, + T value) + where TProvider : ISerializeProvider + { + if (value is null) + { + serializeType.SkipValue(typeInfo, index); + } + else + { + serializeType.WriteBoxedValue(typeInfo, index, value); + } + } +}