// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using MessagePack.Internal;
#pragma warning disable SA1649 // File name should match first type name
namespace MessagePack.Formatters
{
// NET40 -> BigInteger, Complex, Tuple
///
/// Serializes a array as a bin type.
/// Deserializes a bin type or an array of byte-sized integers into a array.
///
public sealed class ByteArrayFormatter : IMessagePackFormatter
{
public static readonly ByteArrayFormatter Instance = new ByteArrayFormatter();
private ByteArrayFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, byte[] value, MessagePackSerializerOptions options)
{
writer.Write(value);
}
public byte[] Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.NextMessagePackType == MessagePackType.Array)
{
int len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty();
}
byte[] array = new byte[len];
options.Security.DepthStep(ref reader);
try
{
for (int i = 0; i < len; i++)
{
reader.CancellationToken.ThrowIfCancellationRequested();
array[i] = reader.ReadByte();
}
}
finally
{
reader.Depth--;
}
return array;
}
else
{
return reader.ReadBytes()?.ToArray();
}
}
}
public sealed class NullableStringFormatter : IMessagePackFormatter
{
public static readonly NullableStringFormatter Instance = new NullableStringFormatter();
private NullableStringFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, string value, MessagePackSerializerOptions options)
{
writer.Write(value);
}
public string Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
return reader.ReadString();
}
}
public sealed class NullableStringArrayFormatter : IMessagePackFormatter
{
public static readonly NullableStringArrayFormatter Instance = new NullableStringArrayFormatter();
private NullableStringArrayFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, String[] value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
}
else
{
writer.WriteArrayHeader(value.Length);
for (int i = 0; i < value.Length; i++)
{
writer.Write(value[i]);
}
}
}
public String[] Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty();
}
var array = new String[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadString();
}
return array;
}
}
public sealed class DecimalFormatter : IMessagePackFormatter
{
public static readonly DecimalFormatter Instance = new DecimalFormatter();
private DecimalFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, decimal value, MessagePackSerializerOptions options)
{
var dest = writer.GetSpan(MessagePackRange.MaxFixStringLength);
if (System.Buffers.Text.Utf8Formatter.TryFormat(value, dest.Slice(1), out var written))
{
// write header
dest[0] = (byte)(MessagePackCode.MinFixStr | written);
writer.Advance(written + 1);
}
else
{
// reset writer's span previously acquired that does not use
writer.Advance(0);
writer.Write(value.ToString(CultureInfo.InvariantCulture));
}
}
public decimal Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (!(reader.ReadStringSequence() is ReadOnlySequence sequence))
{
throw new MessagePackSerializationException(string.Format("Unexpected msgpack code {0} ({1}) encountered.", MessagePackCode.Nil, MessagePackCode.ToFormatName(MessagePackCode.Nil)));
}
if (sequence.IsSingleSegment)
{
var span = sequence.First.Span;
if (System.Buffers.Text.Utf8Parser.TryParse(span, out decimal result, out var bytesConsumed))
{
if (span.Length != bytesConsumed)
{
throw new MessagePackSerializationException("Unexpected length of string.");
}
return result;
}
}
else
{
// sequence.Length is not free
var seqLen = (int)sequence.Length;
if (seqLen < 128)
{
Span span = stackalloc byte[seqLen];
sequence.CopyTo(span);
if (System.Buffers.Text.Utf8Parser.TryParse(span, out decimal result, out var bytesConsumed))
{
if (seqLen != bytesConsumed)
{
throw new MessagePackSerializationException("Unexpected length of string.");
}
return result;
}
}
else
{
var rentArray = ArrayPool.Shared.Rent(seqLen);
try
{
sequence.CopyTo(rentArray);
if (System.Buffers.Text.Utf8Parser.TryParse(rentArray.AsSpan(0, seqLen), out decimal result, out var bytesConsumed))
{
if (seqLen != bytesConsumed)
{
throw new MessagePackSerializationException("Unexpected length of string.");
}
return result;
}
}
finally
{
ArrayPool.Shared.Return(rentArray);
}
}
}
throw new MessagePackSerializationException("Can't parse to decimal, input string was not in a correct format.");
}
}
public sealed class TimeSpanFormatter : IMessagePackFormatter
{
public static readonly IMessagePackFormatter Instance = new TimeSpanFormatter();
private TimeSpanFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, TimeSpan value, MessagePackSerializerOptions options)
{
writer.Write(value.Ticks);
return;
}
public TimeSpan Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
return new TimeSpan(reader.ReadInt64());
}
}
public sealed class DateTimeOffsetFormatter : IMessagePackFormatter
{
public static readonly IMessagePackFormatter Instance = new DateTimeOffsetFormatter();
private DateTimeOffsetFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, DateTimeOffset value, MessagePackSerializerOptions options)
{
writer.WriteArrayHeader(2);
writer.Write(new DateTime(value.Ticks, DateTimeKind.Utc)); // current ticks as is
writer.Write((short)value.Offset.TotalMinutes); // offset is normalized in minutes
return;
}
public DateTimeOffset Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
var count = reader.ReadArrayHeader();
if (count != 2)
{
throw new MessagePackSerializationException("Invalid DateTimeOffset format.");
}
DateTime utc = reader.ReadDateTime();
var dtOffsetMinutes = reader.ReadInt16();
return new DateTimeOffset(utc.Ticks, TimeSpan.FromMinutes(dtOffsetMinutes));
}
}
public sealed class GuidFormatter : IMessagePackFormatter
{
public static readonly IMessagePackFormatter Instance = new GuidFormatter();
private GuidFormatter()
{
}
public unsafe void Serialize(ref MessagePackWriter writer, Guid value, MessagePackSerializerOptions options)
{
byte* pBytes = stackalloc byte[36];
Span bytes = new Span(pBytes, 36);
new GuidBits(ref value).Write(bytes);
writer.WriteString(bytes);
}
public Guid Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
ReadOnlySequence segment = reader.ReadStringSequence().Value;
if (segment.Length != 36)
{
throw new MessagePackSerializationException("Unexpected length of string.");
}
GuidBits result;
if (segment.IsSingleSegment)
{
result = new GuidBits(segment.First.Span);
}
else
{
Span bytes = stackalloc byte[36];
segment.CopyTo(bytes);
result = new GuidBits(bytes);
}
return result.Value;
}
}
public sealed class UriFormatter : IMessagePackFormatter
{
public static readonly IMessagePackFormatter Instance = new UriFormatter();
private UriFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, Uri value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
}
else
{
writer.Write(value.OriginalString);
}
}
public Uri Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}
else
{
return new Uri(reader.ReadString(), UriKind.RelativeOrAbsolute);
}
}
}
public sealed class VersionFormatter : IMessagePackFormatter
{
public static readonly IMessagePackFormatter Instance = new VersionFormatter();
private VersionFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, Version value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
}
else
{
writer.Write(value.ToString());
}
}
public Version Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}
else
{
return new Version(reader.ReadString());
}
}
}
public sealed class KeyValuePairFormatter : IMessagePackFormatter>
{
public void Serialize(ref MessagePackWriter writer, KeyValuePair value, MessagePackSerializerOptions options)
{
writer.WriteArrayHeader(2);
IFormatterResolver resolver = options.Resolver;
resolver.GetFormatterWithVerify().Serialize(ref writer, value.Key, options);
resolver.GetFormatterWithVerify().Serialize(ref writer, value.Value, options);
return;
}
public KeyValuePair Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
var count = reader.ReadArrayHeader();
if (count != 2)
{
throw new MessagePackSerializationException("Invalid KeyValuePair format.");
}
IFormatterResolver resolver = options.Resolver;
options.Security.DepthStep(ref reader);
try
{
TKey key = resolver.GetFormatterWithVerify().Deserialize(ref reader, options);
TValue value = resolver.GetFormatterWithVerify().Deserialize(ref reader, options);
return new KeyValuePair(key, value);
}
finally
{
reader.Depth--;
}
}
}
public sealed class StringBuilderFormatter : IMessagePackFormatter
{
public static readonly IMessagePackFormatter Instance = new StringBuilderFormatter();
private StringBuilderFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, StringBuilder value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
}
else
{
writer.Write(value.ToString());
}
}
public StringBuilder Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}
else
{
return new StringBuilder(reader.ReadString());
}
}
}
public sealed class BitArrayFormatter : IMessagePackFormatter
{
public static readonly IMessagePackFormatter Instance = new BitArrayFormatter();
private BitArrayFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, BitArray value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
}
else
{
var len = value.Length;
writer.WriteArrayHeader(len);
for (int i = 0; i < len; i++)
{
writer.Write(value.Get(i));
}
return;
}
}
public BitArray Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}
else
{
var len = reader.ReadArrayHeader();
var array = new BitArray(len);
for (int i = 0; i < len; i++)
{
array[i] = reader.ReadBoolean();
}
return array;
}
}
}
public sealed class BigIntegerFormatter : IMessagePackFormatter
{
public static readonly IMessagePackFormatter Instance = new BigIntegerFormatter();
private BigIntegerFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, System.Numerics.BigInteger value, MessagePackSerializerOptions options)
{
#if NETCOREAPP
if (!writer.OldSpec)
{
// try to get bin8 buffer.
var span = writer.GetSpan(byte.MaxValue);
if (value.TryWriteBytes(span.Slice(2), out var written))
{
span[0] = MessagePackCode.Bin8;
span[1] = (byte)written;
writer.Advance(written + 2);
return;
}
else
{
// reset writer's span previously acquired that does not use
writer.Advance(0);
}
}
#endif
writer.Write(value.ToByteArray());
return;
}
public System.Numerics.BigInteger Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
ReadOnlySequence bytes = reader.ReadBytes().Value;
#if NETCOREAPP
if (bytes.IsSingleSegment)
{
return new System.Numerics.BigInteger(bytes.First.Span);
}
else
{
byte[] bytesArray = ArrayPool.Shared.Rent((int)bytes.Length);
try
{
bytes.CopyTo(bytesArray);
return new System.Numerics.BigInteger(bytesArray.AsSpan(0, (int)bytes.Length));
}
finally
{
ArrayPool.Shared.Return(bytesArray);
}
}
#else
return new System.Numerics.BigInteger(bytes.ToArray());
#endif
}
}
public sealed class ComplexFormatter : IMessagePackFormatter
{
public static readonly IMessagePackFormatter Instance = new ComplexFormatter();
private ComplexFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, System.Numerics.Complex value, MessagePackSerializerOptions options)
{
writer.WriteArrayHeader(2);
writer.Write(value.Real);
writer.Write(value.Imaginary);
return;
}
public System.Numerics.Complex Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
var count = reader.ReadArrayHeader();
if (count != 2)
{
throw new MessagePackSerializationException("Invalid Complex format.");
}
var real = reader.ReadDouble();
var imaginary = reader.ReadDouble();
return new System.Numerics.Complex(real, imaginary);
}
}
public sealed class LazyFormatter : IMessagePackFormatter>
{
public void Serialize(ref MessagePackWriter writer, Lazy value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
}
else
{
IFormatterResolver resolver = options.Resolver;
resolver.GetFormatterWithVerify().Serialize(ref writer, value.Value, options);
}
}
public Lazy Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}
else
{
options.Security.DepthStep(ref reader);
try
{
// deserialize immediately(no delay, because capture byte[] causes memory leak)
IFormatterResolver resolver = options.Resolver;
T v = resolver.GetFormatterWithVerify().Deserialize(ref reader, options);
return new Lazy(() => v);
}
finally
{
reader.Depth--;
}
}
}
}
///
/// Serializes any instance of by its value.
///
/// The class itself or a derived type.
public sealed class TypeFormatter : IMessagePackFormatter
where T : Type
{
public static readonly IMessagePackFormatter Instance = new TypeFormatter();
private TypeFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options)
{
if (value is null)
{
writer.WriteNil();
}
else
{
writer.Write(value.AssemblyQualifiedName);
}
}
public T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}
return (T)Type.GetType(reader.ReadString(), throwOnError: true);
}
}
#if NET5_0_OR_GREATER
public sealed class HalfFormatter : IMessagePackFormatter
{
public static readonly IMessagePackFormatter Instance = new HalfFormatter();
private HalfFormatter()
{
}
public void Serialize(ref MessagePackWriter writer, Half value, MessagePackSerializerOptions options)
{
writer.Write((float)value);
}
public Half Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
return (Half)reader.ReadSingle();
}
}
#endif
}