using System.Buffers;
using System.Runtime.CompilerServices;
using AyCode.Core.Compression;
using AyCode.Core.Extensions;
using AyCode.Core.Serializers.Binaries;
using AyCode.Core.Serializers.Jsons;
namespace AyCode.Services.SignalRs;
///
/// Centralized helper for SignalR serialization operations.
/// Provides optimized primitives for JSON/Binary serialization with pooled buffers.
///
public static class SignalRSerializationHelper
{
// Pre-boxed boolean values to avoid repeated boxing
private static readonly string JsonTrue = "true";
private static readonly string JsonFalse = "false";
#region Primitive JSON Serialization
///
/// Serialize a primitive value to JSON string with minimal overhead.
/// Falls back to full JSON serialization for complex types.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SerializePrimitiveToJson(object value)
{
return value switch
{
int i => i.ToString(),
long l => l.ToString(),
Guid g => SerializeGuidToJson(g),
bool b => b ? JsonTrue : JsonFalse,
// Strings need proper JSON escaping for special characters
string => value.ToJson(),
_ => value.ToJson()
};
}
///
/// Serialize a Guid to JSON string with pre-allocated buffer.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SerializeGuidToJson(Guid g)
{
// Pre-allocate exact size: 38 chars = 2 quotes + 36 guid chars
return string.Create(38, g, static (span, guid) =>
{
span[0] = '"';
guid.TryFormat(span[1..], out _);
span[37] = '"';
});
}
#endregion
#region Binary Serialization
///
/// Serialize object to binary using pooled ArrayBufferWriter.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] SerializeToBinary(T value, AcBinarySerializerOptions? options = null)
{
var writer = new ArrayBufferWriter(256);
value.ToBinary(writer, options ?? AcBinarySerializerOptions.Default);
return writer.WrittenSpan.ToArray();
}
///
/// Serialize object to binary and write to existing ArrayBufferWriter.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SerializeToBinary(T value, ArrayBufferWriter writer, AcBinarySerializerOptions? options = null)
{
value.ToBinary(writer, options ?? AcBinarySerializerOptions.Default);
}
///
/// Deserialize binary data to object.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? DeserializeFromBinary(byte[] data)
{
return data.BinaryTo();
}
///
/// Deserialize binary data from ReadOnlySpan.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? DeserializeFromBinary(ReadOnlySpan data)
{
return data.BinaryTo();
}
#endregion
#region JSON Serialization with Brotli
///
/// Serialize object to JSON and compress with Brotli.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] SerializeToCompressedJson(T value, AcJsonSerializerOptions? options = null)
{
var json = value.ToJson(options ?? AcJsonSerializerOptions.Default);
return GzipHelper.Compress(json);
}
///
/// Decompress Brotli data and deserialize JSON to object.
/// Uses pooled buffer for decompression.
///
public static T? DeserializeFromCompressedJson(byte[] compressedData)
{
var (buffer, length) = GzipHelper.DecompressToRentedBuffer(compressedData.AsSpan());
try
{
return AcJsonDeserializer.Deserialize(new ReadOnlySpan(buffer, 0, length));
}
finally
{
ArrayPool.Shared.Return(buffer);
}
}
///
/// Decompress Brotli data to rented buffer for direct processing.
/// Caller must return buffer to ArrayPool.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (byte[] Buffer, int Length) DecompressToRentedBuffer(byte[] compressedData)
{
return GzipHelper.DecompressToRentedBuffer(compressedData.AsSpan());
}
#endregion
#region Response Data Helpers
///
/// Check if string appears to be valid JSON (starts with { or [).
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsValidJsonString(ReadOnlySpan text)
{
var trimmed = text.Trim();
return trimmed.Length > 1 &&
(trimmed[0] == '{' || trimmed[0] == '[') &&
(trimmed[^1] == '}' || trimmed[^1] == ']');
}
///
/// Create response binary data based on serializer type.
///
public static byte[]? CreateResponseData(object? responseData, AcSerializerOptions serializerOptions)
{
if (responseData == null)
return null;
if (serializerOptions.SerializerType == AcSerializerType.Binary)
{
if (responseData is byte[] byteData)
return byteData;
var binaryOptions = serializerOptions as AcBinarySerializerOptions ?? AcBinarySerializerOptions.Default;
return SerializeToBinary(responseData, binaryOptions);
}
// JSON mode with Brotli compression
string json;
if (responseData is string strData && IsValidJsonString(strData.AsSpan()))
{
json = strData;
}
else
{
var jsonOptions = serializerOptions as AcJsonSerializerOptions ?? AcJsonSerializerOptions.Default;
json = responseData.ToJson(jsonOptions);
}
return GzipHelper.Compress(json);
}
#endregion
}