| // 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. |
| |
| using System.Runtime.CompilerServices; |
| |
| namespace Apache.Fory; |
| |
| public static class UnknownCaseSerializer |
| { |
| [MethodImpl(MethodImplOptions.NoInlining)] |
| public static void WritePayload(WriteContext context, UnknownCase unknownCase) |
| { |
| // Wire order is ref metadata first, then Any type metadata, then value bytes. Numeric |
| // Any payloads are scalar values, so replay writes NotNullValue plus the original type id. |
| if (WriteTypedPayload(context, (TypeId)unknownCase.TypeId, unknownCase.Value)) |
| { |
| return; |
| } |
| |
| DynamicAnyCodec.WriteAny(context, unknownCase.Value, RefMode.Tracking, true, false); |
| } |
| |
| [MethodImpl(MethodImplOptions.NoInlining)] |
| public static UnknownCase ReadPayload(ReadContext context, int caseId) |
| { |
| RefFlag flag = context.RefReader.ReadRefFlag(context.Reader); |
| switch (flag) |
| { |
| case RefFlag.Null: |
| return UnknownCase.FromRuntime(caseId, (uint)TypeId.Unknown, null); |
| case RefFlag.Ref: |
| { |
| uint refId = context.RefReader.ReadRefId(context.Reader); |
| return UnknownCase.FromRuntime(caseId, (uint)TypeId.Unknown, context.RefReader.GetRefValue(refId)); |
| } |
| case RefFlag.RefValue: |
| { |
| uint reservedRefId = context.RefReader.ReserveRefId(); |
| (uint typeId, object? value) = ReadNonNullPayload(context, reservedRefId); |
| return UnknownCase.FromRuntime(caseId, typeId, value); |
| } |
| case RefFlag.NotNullValue: |
| { |
| (uint typeId, object? value) = ReadNonNullPayload(context); |
| return UnknownCase.FromRuntime(caseId, typeId, value); |
| } |
| default: |
| throw new RefException($"invalid ref flag {(sbyte)flag}"); |
| } |
| } |
| |
| private static bool WriteTypedPayload(WriteContext context, TypeId typeId, object? value) |
| { |
| if (value is null) |
| { |
| return false; |
| } |
| |
| switch (typeId) |
| { |
| case TypeId.Bool when value is bool typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteUInt8(typed ? (byte)1 : (byte)0); |
| return true; |
| case TypeId.Int8 when value is sbyte typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteInt8(typed); |
| return true; |
| case TypeId.UInt8 when value is byte typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteUInt8(typed); |
| return true; |
| case TypeId.Int16 when value is short typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteInt16(typed); |
| return true; |
| case TypeId.UInt16 when value is ushort typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteUInt16(typed); |
| return true; |
| case TypeId.Int32 when value is int typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteInt32(typed); |
| return true; |
| case TypeId.VarInt32 when value is int typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteVarInt32(typed); |
| return true; |
| case TypeId.UInt32 when value is uint typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteUInt32(typed); |
| return true; |
| case TypeId.VarUInt32 when value is uint typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteVarUInt32(typed); |
| return true; |
| case TypeId.Int64 when value is long typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteInt64(typed); |
| return true; |
| case TypeId.VarInt64 when value is long typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteVarInt64(typed); |
| return true; |
| case TypeId.TaggedInt64 when value is long typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteTaggedInt64(typed); |
| return true; |
| case TypeId.UInt64 when value is ulong typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteUInt64(typed); |
| return true; |
| case TypeId.VarUInt64 when value is ulong typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteVarUInt64(typed); |
| return true; |
| case TypeId.TaggedUInt64 when value is ulong typed: |
| WriteRefAndType(context, typeId); |
| context.Writer.WriteTaggedUInt64(typed); |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| private static void WriteRefAndType(WriteContext context, TypeId typeId) |
| { |
| context.Writer.WriteInt8((sbyte)RefFlag.NotNullValue); |
| context.Writer.WriteUInt8((byte)typeId); |
| } |
| |
| private static (uint TypeId, object? Value) ReadNonNullPayload(ReadContext context) |
| { |
| return ReadNonNullPayload(context, hasRef: false, refId: 0); |
| } |
| |
| private static (uint TypeId, object? Value) ReadNonNullPayload(ReadContext context, uint refId) |
| { |
| return ReadNonNullPayload(context, hasRef: true, refId); |
| } |
| |
| private static (uint TypeId, object? Value) ReadNonNullPayload(ReadContext context, bool hasRef, uint refId) |
| { |
| // UnknownCase owns the union payload envelope only. The envelope is not |
| // a nested dynamic value, so depth checks belong to the decoded payload |
| // serializer or the final root-context reset, not this carrier reader. |
| TypeInfo typeInfo = context.TypeResolver.ReadAnyTypeInfo(context); |
| object? value = hasRef |
| ? context.TypeResolver.ReadAnyValue(typeInfo, context, refId) |
| : context.TypeResolver.ReadAnyValue(typeInfo, context); |
| return ((uint)(typeInfo.WireTypeId ?? typeInfo.BuiltInTypeId ?? TypeId.Unknown), value); |
| } |
| } |