blob: d2bc727c208e582fdd655dc13f994a8b5f82ad5f [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
namespace Apache.Fory;
/// <summary>
/// Base class for custom serializers.
/// </summary>
/// <typeparam name="T">Runtime value type handled by this serializer.</typeparam>
public abstract class Serializer<T>
{
/// <summary>
/// Gets the default value returned when a null marker is read for this serializer.
/// </summary>
public virtual T DefaultValue => default!;
internal object? DefaultObject => DefaultValue;
/// <summary>
/// Writes the serializer-specific payload body.
/// </summary>
/// <param name="context">Write context.</param>
/// <param name="value">Value to encode.</param>
/// <param name="hasGenerics">Whether generic type metadata is present for the current field path.</param>
public abstract void WriteData(WriteContext context, in T value, bool hasGenerics);
/// <summary>
/// Reads the serializer-specific payload body.
/// </summary>
/// <param name="context">Read context.</param>
/// <returns>Decoded value.</returns>
public abstract T ReadData(ReadContext context);
/// <summary>
/// Writes ref metadata and optional type metadata, then delegates to <see cref="WriteData"/>.
/// </summary>
/// <param name="context">Write context.</param>
/// <param name="value">Value to write.</param>
/// <param name="refMode">Ref handling mode.</param>
/// <param name="writeTypeInfo">Whether type metadata should be written.</param>
/// <param name="hasGenerics">Whether generic type metadata is present for the current field path.</param>
public virtual void Write(WriteContext context, in T value, RefMode refMode, bool writeTypeInfo, bool hasGenerics)
{
if (refMode != RefMode.None)
{
bool wroteTrackingRefFlag = false;
if (refMode == RefMode.Tracking &&
value is object obj)
{
if (context.RefWriter.TryWriteRef(context.Writer, obj))
{
return;
}
wroteTrackingRefFlag = true;
}
if (!wroteTrackingRefFlag)
{
if (value is null)
{
context.Writer.WriteInt8((sbyte)RefFlag.Null);
return;
}
context.Writer.WriteInt8((sbyte)RefFlag.NotNullValue);
}
}
if (writeTypeInfo)
{
context.TypeResolver.WriteTypeInfo(this, context);
}
WriteData(context, value, hasGenerics);
}
/// <summary>
/// Reads ref metadata and optional type metadata, then delegates to <see cref="ReadData"/>.
/// </summary>
/// <param name="context">Read context.</param>
/// <param name="refMode">Ref handling mode.</param>
/// <param name="readTypeInfo">Whether type metadata should be read.</param>
/// <returns>Decoded value.</returns>
public virtual T Read(ReadContext context, RefMode refMode, bool readTypeInfo)
{
if (refMode != RefMode.None)
{
RefFlag flag = context.RefReader.ReadRefFlag(context.Reader);
switch (flag)
{
case RefFlag.Null:
return DefaultValue;
case RefFlag.Ref:
{
uint refId = context.RefReader.ReadRefId(context.Reader);
return context.RefReader.GetRef<T>(refId);
}
case RefFlag.RefValue:
{
uint reservedRefId = context.RefReader.ReserveRefId();
context.SetReservedRefId(reservedRefId);
try
{
if (readTypeInfo)
{
context.TypeResolver.ReadTypeInfo(this, context);
}
T value = ReadData(context);
context.StoreRef(value);
return value;
}
finally
{
context.ClearReservedRefId();
}
}
case RefFlag.NotNullValue:
break;
default:
throw new RefException($"invalid ref flag {(sbyte)flag}");
}
}
if (readTypeInfo)
{
context.TypeResolver.ReadTypeInfo(this, context);
}
return ReadData(context);
}
}