You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
361 lines
13 KiB
361 lines
13 KiB
using MessagePack.Internal; |
|
using System; |
|
using System.IO; |
|
using MessagePack.LZ4; |
|
|
|
namespace MessagePack |
|
{ |
|
/// <summary> |
|
/// LZ4 Compressed special serializer. |
|
/// </summary> |
|
public static partial class LZ4MessagePackSerializer |
|
{ |
|
public const sbyte ExtensionTypeCode = 99; |
|
|
|
public const int NotCompressionSize = 64; |
|
|
|
/// <summary> |
|
/// Serialize to binary with default resolver. |
|
/// </summary> |
|
public static byte[] Serialize<T>(T obj) |
|
{ |
|
return Serialize(obj, null); |
|
} |
|
|
|
/// <summary> |
|
/// Serialize to binary with specified resolver. |
|
/// </summary> |
|
public static byte[] Serialize<T>(T obj, IFormatterResolver resolver) |
|
{ |
|
if (resolver == null) resolver = MessagePackSerializer.DefaultResolver; |
|
var buffer = SerializeCore(obj, resolver); |
|
|
|
return MessagePackBinary.FastCloneWithResize(buffer.Array, buffer.Count); |
|
} |
|
|
|
/// <summary> |
|
/// Serialize to stream. |
|
/// </summary> |
|
public static void Serialize<T>(Stream stream, T obj) |
|
{ |
|
Serialize(stream, obj, null); |
|
} |
|
|
|
/// <summary> |
|
/// Serialize to stream with specified resolver. |
|
/// </summary> |
|
public static void Serialize<T>(Stream stream, T obj, IFormatterResolver resolver) |
|
{ |
|
if (resolver == null) resolver = MessagePackSerializer.DefaultResolver; |
|
var buffer = SerializeCore(obj, resolver); |
|
|
|
stream.Write(buffer.Array, 0, buffer.Count); |
|
} |
|
|
|
public static int SerializeToBlock<T>(ref byte[] bytes, int offset, T obj, IFormatterResolver resolver) |
|
{ |
|
var serializedData = MessagePackSerializer.SerializeUnsafe(obj, resolver); |
|
|
|
if (serializedData.Count < NotCompressionSize) |
|
{ |
|
// can't write direct, shoganai... |
|
MessagePackBinary.EnsureCapacity(ref bytes, offset, serializedData.Count); |
|
Buffer.BlockCopy(serializedData.Array, serializedData.Offset, bytes, offset, serializedData.Count); |
|
return serializedData.Count; |
|
} |
|
else |
|
{ |
|
var maxOutCount = LZ4Codec.MaximumOutputLength(serializedData.Count); |
|
|
|
MessagePackBinary.EnsureCapacity(ref bytes, offset, 6 + 5 + maxOutCount); // (ext header size + fixed length size) |
|
|
|
// acquire ext header position |
|
var extHeaderOffset = offset; |
|
offset += (6 + 5); |
|
|
|
// write body |
|
var lz4Length = LZ4Codec.Encode(serializedData.Array, serializedData.Offset, serializedData.Count, bytes, offset, bytes.Length - offset); |
|
|
|
// write extension header(always 6 bytes) |
|
extHeaderOffset += MessagePackBinary.WriteExtensionFormatHeaderForceExt32Block(ref bytes, extHeaderOffset, (sbyte)ExtensionTypeCode, lz4Length + 5); |
|
|
|
// write length(always 5 bytes) |
|
MessagePackBinary.WriteInt32ForceInt32Block(ref bytes, extHeaderOffset, serializedData.Count); |
|
|
|
return 6 + 5 + lz4Length; |
|
} |
|
} |
|
|
|
public static byte[] ToLZ4Binary(ArraySegment<byte> messagePackBinary) |
|
{ |
|
var buffer = ToLZ4BinaryCore(messagePackBinary); |
|
return MessagePackBinary.FastCloneWithResize(buffer.Array, buffer.Count); |
|
} |
|
|
|
static ArraySegment<byte> SerializeCore<T>(T obj, IFormatterResolver resolver) |
|
{ |
|
var serializedData = MessagePackSerializer.SerializeUnsafe(obj, resolver); |
|
return ToLZ4BinaryCore(serializedData); |
|
} |
|
|
|
static ArraySegment<byte> ToLZ4BinaryCore(ArraySegment<byte> serializedData) |
|
{ |
|
if (serializedData.Count < NotCompressionSize) |
|
{ |
|
return serializedData; |
|
} |
|
else |
|
{ |
|
var offset = 0; |
|
var buffer = LZ4MemoryPool.GetBuffer(); |
|
var maxOutCount = LZ4Codec.MaximumOutputLength(serializedData.Count); |
|
if (buffer.Length + 6 + 5 < maxOutCount) // (ext header size + fixed length size) |
|
{ |
|
buffer = new byte[6 + 5 + maxOutCount]; |
|
} |
|
|
|
// acquire ext header position |
|
var extHeaderOffset = offset; |
|
offset += (6 + 5); |
|
|
|
// write body |
|
var lz4Length = LZ4Codec.Encode(serializedData.Array, serializedData.Offset, serializedData.Count, buffer, offset, buffer.Length - offset); |
|
|
|
// write extension header(always 6 bytes) |
|
extHeaderOffset += MessagePackBinary.WriteExtensionFormatHeaderForceExt32Block(ref buffer, extHeaderOffset, (sbyte)ExtensionTypeCode, lz4Length + 5); |
|
|
|
// write length(always 5 bytes) |
|
MessagePackBinary.WriteInt32ForceInt32Block(ref buffer, extHeaderOffset, serializedData.Count); |
|
|
|
return new ArraySegment<byte>(buffer, 0, 6 + 5 + lz4Length); |
|
} |
|
} |
|
|
|
public static T Deserialize<T>(byte[] bytes) |
|
{ |
|
return Deserialize<T>(bytes, null); |
|
} |
|
|
|
public static T Deserialize<T>(byte[] bytes, IFormatterResolver resolver) |
|
{ |
|
return DeserializeCore<T>(new ArraySegment<byte>(bytes, 0, bytes.Length), resolver); |
|
} |
|
|
|
public static T Deserialize<T>(ArraySegment<byte> bytes) |
|
{ |
|
return DeserializeCore<T>(bytes, null); |
|
} |
|
|
|
public static T Deserialize<T>(ArraySegment<byte> bytes, IFormatterResolver resolver) |
|
{ |
|
return DeserializeCore<T>(bytes, resolver); |
|
} |
|
|
|
public static T Deserialize<T>(Stream stream) |
|
{ |
|
return Deserialize<T>(stream, null); |
|
} |
|
|
|
public static T Deserialize<T>(Stream stream, IFormatterResolver resolver) |
|
{ |
|
return Deserialize<T>(stream, resolver, false); |
|
} |
|
|
|
public static T Deserialize<T>(Stream stream, bool readStrict) |
|
{ |
|
return Deserialize<T>(stream, MessagePackSerializer.DefaultResolver, readStrict); |
|
} |
|
|
|
public static T Deserialize<T>(Stream stream, IFormatterResolver resolver, bool readStrict) |
|
{ |
|
if (!readStrict) |
|
{ |
|
var buffer = MessagePack.Internal.InternalMemoryPool.GetBuffer(); // use MessagePackSerializer.Pool! |
|
var len = FillFromStream(stream, ref buffer); |
|
return DeserializeCore<T>(new ArraySegment<byte>(buffer, 0, len), resolver); |
|
} |
|
else |
|
{ |
|
int blockSize; |
|
var bytes = MessagePackBinary.ReadMessageBlockFromStreamUnsafe(stream, false, out blockSize); |
|
return DeserializeCore<T>(new ArraySegment<byte>(bytes, 0, blockSize), resolver); |
|
} |
|
} |
|
|
|
public static byte[] Decode(Stream stream, bool readStrict = false) |
|
{ |
|
if (!readStrict) |
|
{ |
|
var buffer = MessagePack.Internal.InternalMemoryPool.GetBuffer(); // use MessagePackSerializer.Pool! |
|
var len = FillFromStream(stream, ref buffer); |
|
return Decode(new ArraySegment<byte>(buffer, 0, len)); |
|
} |
|
else |
|
{ |
|
int blockSize; |
|
var bytes = MessagePackBinary.ReadMessageBlockFromStreamUnsafe(stream, false, out blockSize); |
|
return Decode(new ArraySegment<byte>(bytes, 0, blockSize)); |
|
} |
|
} |
|
|
|
public static byte[] Decode(byte[] bytes) |
|
{ |
|
return Decode(new ArraySegment<byte>(bytes, 0, bytes.Length)); |
|
} |
|
|
|
public static byte[] Decode(ArraySegment<byte> bytes) |
|
{ |
|
int readSize; |
|
if (MessagePackBinary.GetMessagePackType(bytes.Array, bytes.Offset) == MessagePackType.Extension) |
|
{ |
|
var header = MessagePackBinary.ReadExtensionFormatHeader(bytes.Array, bytes.Offset, out readSize); |
|
if (header.TypeCode == ExtensionTypeCode) |
|
{ |
|
// decode lz4 |
|
var offset = bytes.Offset + readSize; |
|
var length = MessagePackBinary.ReadInt32(bytes.Array, offset, out readSize); |
|
offset += readSize; |
|
|
|
var buffer = new byte[length]; // use new buffer. |
|
|
|
// LZ4 Decode |
|
var len = bytes.Count + bytes.Offset - offset; |
|
LZ4Codec.Decode(bytes.Array, offset, len, buffer, 0, length); |
|
|
|
return buffer; |
|
} |
|
} |
|
|
|
if (bytes.Offset == 0 && bytes.Array.Length == bytes.Count) |
|
{ |
|
// return same reference |
|
return bytes.Array; |
|
} |
|
else |
|
{ |
|
var result = new byte[bytes.Count]; |
|
Buffer.BlockCopy(bytes.Array, bytes.Offset, result, 0, result.Length); |
|
return result; |
|
} |
|
} |
|
|
|
|
|
/// <summary> |
|
/// Get the war memory pool byte[]. The result can not share across thread and can not hold and can not call LZ4Deserialize before use it. |
|
/// </summary> |
|
public static byte[] DecodeUnsafe(byte[] bytes) |
|
{ |
|
return DecodeUnsafe(new ArraySegment<byte>(bytes, 0, bytes.Length)); |
|
} |
|
|
|
/// <summary> |
|
/// Get the war memory pool byte[]. The result can not share across thread and can not hold and can not call LZ4Deserialize before use it. |
|
/// </summary> |
|
public static byte[] DecodeUnsafe(ArraySegment<byte> bytes) |
|
{ |
|
int readSize; |
|
if (MessagePackBinary.GetMessagePackType(bytes.Array, bytes.Offset) == MessagePackType.Extension) |
|
{ |
|
var header = MessagePackBinary.ReadExtensionFormatHeader(bytes.Array, bytes.Offset, out readSize); |
|
if (header.TypeCode == ExtensionTypeCode) |
|
{ |
|
// decode lz4 |
|
var offset = bytes.Offset + readSize; |
|
var length = MessagePackBinary.ReadInt32(bytes.Array, offset, out readSize); |
|
offset += readSize; |
|
|
|
var buffer = LZ4MemoryPool.GetBuffer(); // use LZ4 Pool(Unsafe) |
|
if (buffer.Length < length) |
|
{ |
|
buffer = new byte[length]; |
|
} |
|
|
|
// LZ4 Decode |
|
var len = bytes.Count + bytes.Offset - offset; |
|
LZ4Codec.Decode(bytes.Array, offset, len, buffer, 0, length); |
|
|
|
return buffer; // return pooled bytes. |
|
} |
|
} |
|
|
|
if (bytes.Offset == 0 && bytes.Array.Length == bytes.Count) |
|
{ |
|
// return same reference |
|
return bytes.Array; |
|
} |
|
else |
|
{ |
|
var result = new byte[bytes.Count]; |
|
Buffer.BlockCopy(bytes.Array, bytes.Offset, result, 0, result.Length); |
|
return result; |
|
} |
|
} |
|
|
|
static T DeserializeCore<T>(ArraySegment<byte> bytes, IFormatterResolver resolver) |
|
{ |
|
if (resolver == null) resolver = MessagePackSerializer.DefaultResolver; |
|
var formatter = resolver.GetFormatterWithVerify<T>(); |
|
|
|
int readSize; |
|
if (MessagePackBinary.GetMessagePackType(bytes.Array, bytes.Offset) == MessagePackType.Extension) |
|
{ |
|
var header = MessagePackBinary.ReadExtensionFormatHeader(bytes.Array, bytes.Offset, out readSize); |
|
if (header.TypeCode == ExtensionTypeCode) |
|
{ |
|
// decode lz4 |
|
var offset = bytes.Offset + readSize; |
|
var length = MessagePackBinary.ReadInt32(bytes.Array, offset, out readSize); |
|
offset += readSize; |
|
|
|
var buffer = LZ4MemoryPool.GetBuffer(); // use LZ4 Pool |
|
if (buffer.Length < length) |
|
{ |
|
buffer = new byte[length]; |
|
} |
|
|
|
// LZ4 Decode |
|
var len = bytes.Count + bytes.Offset - offset; |
|
LZ4Codec.Decode(bytes.Array, offset, len, buffer, 0, length); |
|
|
|
return formatter.Deserialize(buffer, 0, resolver, out readSize); |
|
} |
|
} |
|
|
|
return formatter.Deserialize(bytes.Array, bytes.Offset, resolver, out readSize); |
|
} |
|
|
|
static int FillFromStream(Stream input, ref byte[] buffer) |
|
{ |
|
int length = 0; |
|
int read; |
|
while ((read = input.Read(buffer, length, buffer.Length - length)) > 0) |
|
{ |
|
length += read; |
|
if (length == buffer.Length) |
|
{ |
|
MessagePackBinary.FastResize(ref buffer, length * 2); |
|
} |
|
} |
|
|
|
return length; |
|
} |
|
} |
|
} |
|
|
|
namespace MessagePack.Internal |
|
{ |
|
internal static class LZ4MemoryPool |
|
{ |
|
[ThreadStatic] |
|
static byte[] lz4buffer = null; |
|
|
|
public static byte[] GetBuffer() |
|
{ |
|
if (lz4buffer == null) |
|
{ |
|
lz4buffer = new byte[LZ4.LZ4Codec.MaximumOutputLength(65536)]; |
|
} |
|
return lz4buffer; |
|
} |
|
} |
|
} |