/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Linq;
using System.IO;
using System.Reflection;
using System.Text;
using System.Collections.Generic;
namespace NetSerializer
{
public static class Primitives
{
internal static MethodInfo GetWritePrimitive(Type type)
{
if (type.IsEnum)
type = type.GetEnumUnderlyingType();
MethodInfo writer = typeof(Primitives).GetMethod("WritePrimitive", BindingFlags.Static | BindingFlags.Public | BindingFlags.ExactBinding, null,
new Type[] { typeof(Stream), type }, null);
if (writer != null)
return writer;
if (type.IsGenericType)
{
var genType = type.GetGenericTypeDefinition();
var mis = typeof(Primitives).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(mi => mi.IsGenericMethod && mi.Name == "WritePrimitive");
foreach (var mi in mis)
{
var p = mi.GetParameters();
if (p.Length != 2)
continue;
if (p[0].ParameterType != typeof(Stream))
continue;
var paramType = p[1].ParameterType;
if (paramType.IsGenericType == false)
continue;
var genParamType = paramType.GetGenericTypeDefinition();
if (genType == genParamType)
return mi;
}
}
return null;
}
internal static MethodInfo GetReadPrimitive(Type type)
{
if (type.IsEnum)
type = type.GetEnumUnderlyingType();
var reader = typeof(Primitives).GetMethod("ReadPrimitive", BindingFlags.Static | BindingFlags.Public | BindingFlags.ExactBinding, null,
new Type[] { typeof(Stream), type.MakeByRefType() }, null);
if (reader != null)
return reader;
if (type.IsGenericType)
{
var genType = type.GetGenericTypeDefinition();
var mis = typeof(Primitives).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(mi => mi.IsGenericMethod && mi.Name == "ReadPrimitive");
foreach (var mi in mis)
{
var p = mi.GetParameters();
if (p.Length != 2)
continue;
if (p[0].ParameterType != typeof(Stream))
continue;
var paramType = p[1].ParameterType;
if (paramType.IsByRef == false)
continue;
paramType = paramType.GetElementType();
if (paramType.IsGenericType == false)
continue;
var genParamType = paramType.GetGenericTypeDefinition();
if (genType == genParamType)
return mi;
}
}
return null;
}
static uint EncodeZigZag32(int n)
{
return (uint)((n << 1) ^ (n >> 31));
}
static ulong EncodeZigZag64(long n)
{
return (ulong)((n << 1) ^ (n >> 63));
}
static int DecodeZigZag32(uint n)
{
return (int)(n >> 1) ^ -(int)(n & 1);
}
static long DecodeZigZag64(ulong n)
{
return (long)(n >> 1) ^ -(long)(n & 1);
}
static uint ReadVarint32(Stream stream)
{
int result = 0;
int offset = 0;
for (; offset < 32; offset += 7)
{
int b = stream.ReadByte();
if (b == -1)
throw new EndOfStreamException();
result |= (b & 0x7f) << offset;
if ((b & 0x80) == 0)
return (uint)result;
}
throw new InvalidDataException();
}
static void WriteVarint32(Stream stream, uint value)
{
for (; value >= 0x80u; value >>= 7)
stream.WriteByte((byte)(value | 0x80u));
stream.WriteByte((byte)value);
}
static ulong ReadVarint64(Stream stream)
{
long result = 0;
int offset = 0;
for (; offset < 64; offset += 7)
{
int b = stream.ReadByte();
if (b == -1)
throw new EndOfStreamException();
result |= ((long)(b & 0x7f)) << offset;
if ((b & 0x80) == 0)
return (ulong)result;
}
throw new InvalidDataException();
}
static void WriteVarint64(Stream stream, ulong value)
{
for (; value >= 0x80u; value >>= 7)
stream.WriteByte((byte)(value | 0x80u));
stream.WriteByte((byte)value);
}
public static void WritePrimitive(Stream stream, bool value)
{
stream.WriteByte(value ? (byte)1 : (byte)0);
}
public static void ReadPrimitive(Stream stream, out bool value)
{
var b = stream.ReadByte();
value = b != 0;
}
public static void WritePrimitive(Stream stream, byte value)
{
stream.WriteByte(value);
}
public static void ReadPrimitive(Stream stream, out byte value)
{
value = (byte)stream.ReadByte();
}
public static void WritePrimitive(Stream stream, sbyte value)
{
stream.WriteByte((byte)value);
}
public static void ReadPrimitive(Stream stream, out sbyte value)
{
value = (sbyte)stream.ReadByte();
}
public static void WritePrimitive(Stream stream, char value)
{
WriteVarint32(stream, value);
}
public static void ReadPrimitive(Stream stream, out char value)
{
value = (char)ReadVarint32(stream);
}
public static void WritePrimitive(Stream stream, ushort value)
{
WriteVarint32(stream, value);
}
public static void ReadPrimitive(Stream stream, out ushort value)
{
value = (ushort)ReadVarint32(stream);
}
public static void WritePrimitive(Stream stream, short value)
{
WriteVarint32(stream, EncodeZigZag32(value));
}
public static void ReadPrimitive(Stream stream, out short value)
{
value = (short)DecodeZigZag32(ReadVarint32(stream));
}
public static void WritePrimitive(Stream stream, uint value)
{
WriteVarint32(stream, value);
}
public static void ReadPrimitive(Stream stream, out uint value)
{
value = ReadVarint32(stream);
}
public static void WritePrimitive(Stream stream, int value)
{
WriteVarint32(stream, EncodeZigZag32(value));
}
public static void ReadPrimitive(Stream stream, out int value)
{
value = DecodeZigZag32(ReadVarint32(stream));
}
public static void WritePrimitive(Stream stream, ulong value)
{
WriteVarint64(stream, value);
}
public static void ReadPrimitive(Stream stream, out ulong value)
{
value = ReadVarint64(stream);
}
public static void WritePrimitive(Stream stream, long value)
{
WriteVarint64(stream, EncodeZigZag64(value));
}
public static void ReadPrimitive(Stream stream, out long value)
{
value = DecodeZigZag64(ReadVarint64(stream));
}
#if !NO_UNSAFE
public static unsafe void WritePrimitive(Stream stream, float value)
{
uint v = *(uint*)(&value);
WriteVarint32(stream, v);
}
public static unsafe void ReadPrimitive(Stream stream, out float value)
{
uint v = ReadVarint32(stream);
value = *(float*)(&v);
}
public static unsafe void WritePrimitive(Stream stream, double value)
{
ulong v = *(ulong*)(&value);
WriteVarint64(stream, v);
}
public static unsafe void ReadPrimitive(Stream stream, out double value)
{
ulong v = ReadVarint64(stream);
value = *(double*)(&v);
}
#else
public static void WritePrimitive(Stream stream, float value)
{
WritePrimitive(stream, (double)value);
}
public static void ReadPrimitive(Stream stream, out float value)
{
double v;
ReadPrimitive(stream, out v);
value = (float)v;
}
public static void WritePrimitive(Stream stream, double value)
{
ulong v = (ulong)BitConverter.DoubleToInt64Bits(value);
WriteVarint64(stream, v);
}
public static void ReadPrimitive(Stream stream, out double value)
{
ulong v = ReadVarint64(stream);
value = BitConverter.Int64BitsToDouble((long)v);
}
#endif
public static void WritePrimitive(Stream stream, DateTime value)
{
long v = value.ToBinary();
WritePrimitive(stream, v);
}
public static void ReadPrimitive(Stream stream, out DateTime value)
{
long v;
ReadPrimitive(stream, out v);
value = DateTime.FromBinary(v);
}
public static void WritePrimitive(Stream stream, string value)
{
if (value == null)
{
WritePrimitive(stream, (uint)0);
return;
}
#if GC_NICE_VERSION
WritePrimitive(stream, (uint)value.Length + 1);
foreach (char c in value)
WritePrimitive(stream, c);
#else
var encoding = new UTF8Encoding(false, true);
int len = encoding.GetByteCount(value);
WritePrimitive(stream, (uint)len + 1);
var buf = new byte[len];
encoding.GetBytes(value, 0, value.Length, buf, 0);
stream.Write(buf, 0, len);
#endif
}
public static void ReadPrimitive(Stream stream, out string value)
{
uint len;
ReadPrimitive(stream, out len);
if (len == 0)
{
value = null;
return;
}
else if (len == 1)
{
value = string.Empty;
return;
}
len -= 1;
#if GC_NICE_VERSION
var arr = new char[len];
for (uint i = 0; i < len; ++i)
ReadPrimitive(stream, out arr[i]);
value = new string(arr);
#else
var encoding = new UTF8Encoding(false, true);
var buf = new byte[len];
int l = 0;
while (l < len)
{
int r = stream.Read(buf, l, (int)len - l);
if (r == 0)
throw new EndOfStreamException();
l += r;
}
value = encoding.GetString(buf);
#endif
}
public static void WritePrimitive(Stream stream, byte[] value)
{
if (value == null)
{
WritePrimitive(stream, (uint)0);
return;
}
WritePrimitive(stream, (uint)value.Length + 1);
stream.Write(value, 0, value.Length);
}
static readonly byte[] s_emptyByteArray = new byte[0];
public static void ReadPrimitive(Stream stream, out byte[] value)
{
uint len;
ReadPrimitive(stream, out len);
if (len == 0)
{
value = null;
return;
}
else if (len == 1)
{
value = s_emptyByteArray;
return;
}
len -= 1;
value = new byte[len];
int l = 0;
while (l < len)
{
int r = stream.Read(value, l, (int)len - l);
if (r == 0)
throw new EndOfStreamException();
l += r;
}
}
public static void WritePrimitive<TKey, TValue>(Stream stream, Dictionary<TKey, TValue> value)
{
var kvpArray = new KeyValuePair<TKey, TValue>[value.Count];
int i = 0;
foreach (var kvp in value)
kvpArray[i++] = kvp;
NetSerializer.Serializer.Serialize(stream, kvpArray);
}
public static void ReadPrimitive<TKey, TValue>(Stream stream, out Dictionary<TKey, TValue> value)
{
var kvpArray = (KeyValuePair<TKey, TValue>[])NetSerializer.Serializer.Deserialize(stream);
value = new Dictionary<TKey, TValue>(kvpArray.Length);
foreach (var kvp in kvpArray)
value.Add(kvp.Key, kvp.Value);
}
}
}