saviine/server/src/System/IO/EndianBinaryWriter.cs

644 lines
22 KiB
C#

using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace System.IO
{
sealed class EndianBinaryWriter : IDisposable
{
private bool disposed;
private byte[] buffer;
private delegate void ArrayReverse(byte[] array, int count);
private static readonly ArrayReverse[] fastReverse = new ArrayReverse[] { null, null, ArrayReverse2, null, ArrayReverse4, null, null, null, ArrayReverse8 };
private static Dictionary<Type, List<Tuple<int, TypeCode>>> parserCache = new Dictionary<Type, List<Tuple<int, TypeCode>>>();
public Stream BaseStream { get; private set; }
public Endianness Endianness { get; set; }
public static Endianness SystemEndianness { get { return BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian; } }
private bool Reverse { get { return SystemEndianness != Endianness; } }
public EndianBinaryWriter(Stream baseStream)
: this(baseStream, Endianness.BigEndian)
{ }
public EndianBinaryWriter(Stream baseStream, Endianness endianness)
{
if (baseStream == null) throw new ArgumentNullException("baseStream");
if (!baseStream.CanWrite) throw new ArgumentException("base stream is not writeable", "baseStream");
BaseStream = baseStream;
Endianness = endianness;
}
~EndianBinaryWriter()
{
Dispose(false);
}
private void WriteBuffer(int bytes, int stride)
{
if (Reverse)
{
if (fastReverse[stride] != null)
fastReverse[stride](buffer, bytes);
else
for (int i = 0; i < bytes; i += stride)
{
Array.Reverse(buffer, i, stride);
}
}
BaseStream.Write(buffer, 0, bytes);
}
private static void ArrayReverse2(byte[] array, int arrayLength)
{
byte temp;
while (arrayLength > 0)
{
temp = array[arrayLength - 2];
array[arrayLength - 2] = array[arrayLength - 1];
array[arrayLength - 1] = temp;
arrayLength -= 2;
}
}
private static void ArrayReverse4(byte[] array, int arrayLength)
{
byte temp;
while (arrayLength > 0)
{
temp = array[arrayLength - 3];
array[arrayLength - 3] = array[arrayLength - 2];
array[arrayLength - 2] = temp;
temp = array[arrayLength - 4];
array[arrayLength - 4] = array[arrayLength - 1];
array[arrayLength - 1] = temp;
arrayLength -= 4;
}
}
private static void ArrayReverse8(byte[] array, int arrayLength)
{
byte temp;
while (arrayLength > 0)
{
temp = array[arrayLength - 5];
array[arrayLength - 5] = array[arrayLength - 4];
array[arrayLength - 4] = temp;
temp = array[arrayLength - 6];
array[arrayLength - 6] = array[arrayLength - 3];
array[arrayLength - 3] = temp;
temp = array[arrayLength - 7];
array[arrayLength - 7] = array[arrayLength - 2];
array[arrayLength - 2] = temp;
temp = array[arrayLength - 8];
array[arrayLength - 8] = array[arrayLength - 1];
array[arrayLength - 1] = temp;
arrayLength -= 8;
}
}
private void CreateBuffer(int size)
{
if (buffer == null || buffer.Length < size)
buffer = new byte[size];
}
public void Write(byte value)
{
CreateBuffer(1);
buffer[0] = value;
WriteBuffer(1, 1);
}
public void Write(byte[] value, int offset, int count)
{
CreateBuffer(count);
Array.Copy(value, offset, buffer, 0, count);
WriteBuffer(count, 1);
}
public void Write(sbyte value)
{
CreateBuffer(1);
unchecked
{
buffer[0] = (byte)value;
}
WriteBuffer(1, 1);
}
public void Write(sbyte[] value, int offset, int count)
{
CreateBuffer(count);
unchecked
{
for (int i = 0; i < count; i++)
{
buffer[i] = (byte)value[i + offset];
}
}
WriteBuffer(count, 1);
}
public void Write(char value, Encoding encoding)
{
int size;
if (encoding == null) throw new ArgumentNullException("encoding");
size = GetEncodingSize(encoding);
CreateBuffer(size);
Array.Copy(encoding.GetBytes(new string(value, 1)), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(char[] value, int offset, int count, Encoding encoding)
{
int size;
if (encoding == null) throw new ArgumentNullException("encoding");
size = GetEncodingSize(encoding);
CreateBuffer(size * count);
Array.Copy(encoding.GetBytes(value, offset, count), 0, buffer, 0, count * size);
WriteBuffer(size * count, size);
}
private static int GetEncodingSize(Encoding encoding)
{
if (encoding == Encoding.UTF8 || encoding == Encoding.ASCII)
return 1;
else if (encoding == Encoding.Unicode || encoding == Encoding.BigEndianUnicode)
return 2;
return 1;
}
public void Write(string value,Encoding encoding, bool nullTerminated)
{
Write(value.ToCharArray(), 0, value.Length, encoding);
if (nullTerminated)
Write('\0', encoding);
}
public void Write(double value)
{
const int size = sizeof(double);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(double[] value, int offset, int count)
{
const int size = sizeof(double);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(Single value)
{
const int size = sizeof(Single);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(Single[] value, int offset, int count)
{
const int size = sizeof(Single);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(Int32 value)
{
const int size = sizeof(Int32);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(Int32[] value, int offset, int count)
{
const int size = sizeof(Int32);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(Int64 value)
{
const int size = sizeof(Int64);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(Int64[] value, int offset, int count)
{
const int size = sizeof(Int64);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(Int16 value)
{
const int size = sizeof(Int16);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(Int16[] value, int offset, int count)
{
const int size = sizeof(Int16);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(UInt16 value)
{
const int size = sizeof(UInt16);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(UInt16[] value, int offset, int count)
{
const int size = sizeof(UInt16);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(UInt32 value)
{
const int size = sizeof(UInt32);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(UInt32[] value, int offset, int count)
{
const int size = sizeof(UInt32);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void Write(UInt64 value)
{
const int size = sizeof(UInt64);
CreateBuffer(size);
Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size);
WriteBuffer(size, size);
}
public void Write(UInt64[] value, int offset, int count)
{
const int size = sizeof(UInt64);
CreateBuffer(size * count);
for (int i = 0; i < count; i++)
{
Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size);
}
WriteBuffer(size * count, size);
}
public void WritePadding(int multiple, byte padding)
{
int length = (int)(BaseStream.Position % multiple);
if (length != 0)
while (length != multiple)
{
BaseStream.WriteByte(padding);
length++;
}
}
public void WritePadding(int multiple, byte padding, long from, int offset)
{
int length = (int)((BaseStream.Position - from) % multiple);
length = (length + offset) % multiple;
if (length != 0)
while (length != multiple)
{
BaseStream.WriteByte(padding);
length++;
}
}
private List<Tuple<int, TypeCode>> GetParser(Type type)
{
List<Tuple<int, TypeCode>> parser;
/* A parser describes how to read in a type in an Endian
* appropriate manner. Basically it describes as series of calls to
* the Read* methods above to parse the structure.
* The parser runs through each element in the list in order. If
* the TypeCode is not Empty then it reads an array of values
* according to the integer. Otherwise it skips a number of bytes. */
try
{
parser = parserCache[type];
}
catch (KeyNotFoundException)
{
parser = new List<Tuple<int, TypeCode>>();
if (Endianness != SystemEndianness)
{
int pos, sz;
pos = 0;
foreach (var item in type.GetFields())
{
int off = Marshal.OffsetOf(type, item.Name).ToInt32();
if (off != pos)
{
parser.Add(new Tuple<int, TypeCode>(off - pos, TypeCode.Empty));
pos = off;
}
switch (Type.GetTypeCode(item.FieldType))
{
case TypeCode.Byte:
case TypeCode.SByte:
pos += 1;
parser.Add(new Tuple<int, TypeCode>(1, Type.GetTypeCode(item.FieldType)));
break;
case TypeCode.Int16:
case TypeCode.UInt16:
pos += 2;
parser.Add(new Tuple<int, TypeCode>(1, Type.GetTypeCode(item.FieldType)));
break;
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Single:
pos += 4;
parser.Add(new Tuple<int, TypeCode>(1, Type.GetTypeCode(item.FieldType)));
break;
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Double:
pos += 8;
parser.Add(new Tuple<int, TypeCode>(1, Type.GetTypeCode(item.FieldType)));
break;
case TypeCode.Object:
if (item.FieldType.IsArray)
{
/* array */
Type elementType;
MarshalAsAttribute[] attrs;
MarshalAsAttribute attr;
attrs = (MarshalAsAttribute[])item.GetCustomAttributes(typeof(MarshalAsAttribute), false);
if (attrs.Length != 1)
throw new ArgumentException(String.Format("Field `{0}' is an array without a MarshalAs attribute.", item.Name), "type");
attr = attrs[0];
if (attr.Value != UnmanagedType.ByValArray)
throw new ArgumentException(String.Format("Field `{0}' is not a ByValArray.", item.Name), "type");
elementType = item.FieldType.GetElementType();
switch (Type.GetTypeCode(elementType))
{
case TypeCode.Byte:
case TypeCode.SByte:
pos += 1 * attr.SizeConst;
parser.Add(new Tuple<int, TypeCode>(attr.SizeConst, Type.GetTypeCode(elementType)));
break;
case TypeCode.Int16:
case TypeCode.UInt16:
pos += 2 * attr.SizeConst;
parser.Add(new Tuple<int, TypeCode>(attr.SizeConst, Type.GetTypeCode(elementType)));
break;
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Single:
pos += 4 * attr.SizeConst;
parser.Add(new Tuple<int, TypeCode>(attr.SizeConst, Type.GetTypeCode(elementType)));
break;
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Double:
pos += 8 * attr.SizeConst;
parser.Add(new Tuple<int, TypeCode>(attr.SizeConst, Type.GetTypeCode(elementType)));
break;
case TypeCode.Object:
/* nested structure */
for (int i = 0; i < attr.SizeConst; i++)
{
pos += Marshal.SizeOf(elementType);
parser.AddRange(GetParser(elementType));
}
break;
default:
break;
}
}
else
{
/* nested structure */
pos += Marshal.SizeOf(item.FieldType);
parser.AddRange(GetParser(item.FieldType));
}
break;
default:
throw new NotImplementedException();
}
}
sz = Marshal.SizeOf(type);
if (sz != pos)
{
parser.Add(new Tuple<int, TypeCode>(sz - pos, TypeCode.Empty));
}
}
else
{
int sz;
sz = Marshal.SizeOf(type);
parser.Add(new Tuple<int, TypeCode>(sz, TypeCode.Byte));
}
parserCache.Add(type, parser);
}
return parser;
}
private void RunParser(List<Tuple<int, TypeCode>> parser, BinaryReader rd)
{
foreach (var item in parser)
{
/* Assumption: Types of the same size can be interchanged. */
switch (item.Item2)
{
case TypeCode.Byte:
case TypeCode.SByte:
Write(rd.ReadBytes(item.Item1), 0, item.Item1);
break;
case TypeCode.Int16:
case TypeCode.UInt16:
for (int i = 0; i < item.Item1; i++)
Write(rd.ReadInt16());
break;
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Single:
for (int i = 0; i < item.Item1; i++)
Write(rd.ReadInt32());
break;
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Double:
for (int i = 0; i < item.Item1; i++)
Write(rd.ReadInt64());
break;
case TypeCode.Empty:
rd.BaseStream.Seek(item.Item1, SeekOrigin.Current);
BaseStream.Seek(item.Item1, SeekOrigin.Current);
break;
default:
throw new NotImplementedException();
}
}
}
public void Write(object structure)
{
List<Tuple<int, TypeCode>> parser;
Type type;
byte[] data;
type = structure.GetType();
parser = GetParser(type);
data = new byte[Marshal.SizeOf(type)];
using (var ms = new MemoryStream(data))
{
using (var rd = new BinaryReader(ms))
{
Marshal.StructureToPtr(structure, Marshal.UnsafeAddrOfPinnedArrayElement(data, 0), true);
RunParser(parser, rd);
}
}
}
public void Write(Array structures)
{
List<Tuple<int, TypeCode>> parser;
Type type;
byte[] data;
type = structures.GetType().GetElementType();
parser = GetParser(type);
data = new byte[Marshal.SizeOf(type)];
using (var ms = new MemoryStream(data))
{
using (var rd = new BinaryReader(ms))
{
foreach (var structure in structures)
{
ms.Seek(0, SeekOrigin.Begin);
Marshal.StructureToPtr(structure, Marshal.UnsafeAddrOfPinnedArrayElement(data, 0), true);
RunParser(parser, rd);
}
}
}
}
public void Close()
{
BaseStream.Close();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
}
BaseStream = null;
buffer = null;
disposed = true;
}
}
}
}