[c#] Как изменить порядок байт при сериализации структуры LE-__m_GT
http://stackoverflow.com/questions/3492304/c-encounter-a-pro...
Неужели нет чего-нибудь типа питоновской библиотерки Struct, в которой всё очень просто
вот тут советуют руками всё преобразовывать Неужели нет чего-нибудь типа питоновской библиотерки Struct, в которой всё очень просто
i - это int(4)
h - это short(2)
>>> from struct import *
>>> pack("ih",1,2)
'\x01\x00\x00\x00\x02\x00'
>>> pack("!ih",1,2)
'\x00\x00\x00\x01\x00\x02'
>>>
private static void RespectEndianness(Type type, byte[] data)
{
/*var fields = type.GetFields.Where(f => f.IsDefined(typeof(EndianAttribute false
.Select(f => new
{
Field = f,
Attribute = (EndianAttribute)f.GetCustomAttributes(typeof(EndianAttribute false)[0],
Offset = Marshal.OffsetOf(type, f.Name).ToInt32
}).ToList;*/
FieldInfo[] fields = type.GetFields;
foreach (FieldInfo field in fields)
{
int offset = Marshal.OffsetOf(type, field.Name).ToInt32;
if (BitConverter.IsLittleEndian)
{
Array.Reverse(data, offset, Marshal.SizeOf(field.FieldType;
}
}
}
public static T BytesToStruct<T>(byte[] rawData) where T : struct
{
T result = default(T);
RespectEndianness(typeof(T rawData);
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject;
result = (T)Marshal.PtrToStructure(rawDataPtr, typeof(T;
}
finally
{
handle.Free;
}
return result;
}
public static byte[] StructToBytes<T>(T data) where T : struct
{
byte[] rawData = new byte[Marshal.SizeOf(data)];
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject;
Marshal.StructureToPtr(data, rawDataPtr, false);
}
finally
{
handle.Free;
}
RespectEndianness(typeof(T rawData);
return rawData;
}
один раз пишутся функции pack/unpack, которые используя reflection, ipaddress.NetworkToHostOrder/HostToNetworkOrder, binaryreader/writer - распаковывают или запаковывают структуры в последовательность байт
вопрос такой.
есть структура
struct myStruct
{
int ID;
short time;
char name[10];
}
хочу, чтобы она занимала ровно 4 + 2 + 10 байт. Как её определить?
Вот это ругается на то, что FieldOffset ==6 для char[]. Нужно 8 ставить.
[StructLayout(LayoutKind.Explicit, Pack = 1, CharSet = CharSet.Ansi)]
public struct Data3
{
[FieldOffset(0)]
public int ID;
[FieldOffset(4)]
public short time;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
[FieldOffset(6)]
public char[] name;
}
можно сделать так
Хочу в итоге получить такой код.
Packet pt = new Packet<Data3>
Data3 vasya = new Data3
one.ID = 20;
one time = 10;
one name = "oooone";
pt.pack(one)
two.ID = 30;
two time = 10;
two name = "twooooo";
pt.pack(two)
Возможно такое?
очень хочется,чтобы всё исследование объекта выполнялось один раз, а в pack и unpack не было проверок на соответсвие типов, приведений и т.д.
Я не силён в терминологии, но надеюсь, что мысль изложил доступно.
[StructLayout(LayoutKind.Explicit, Pack = 1, CharSet = CharSet.Ansi)]
public unsafe struct Data2
{
private const int BUFFER_SIZE = 100;
[FieldOffset(0)]
public int ID;
[FieldOffset(4)]
public short Date;
[FieldOffset(6)]
public fixed byte name[BUFFER_SIZE];
}
сделай свой pack/unpack через reflection
про unsafe выше - это я не туда полез, да.
это необходимо, чтобы поля не перемешивались, когда их получаешь через x.GetType.GetFields
Погоди, а как в структуре задать фиксированную длину byte[] ?
я хочу, чтобы в pack /unpack эти функции не вызывались.
У меня есть идея, но она тянет на какой-то жёсткий хак.
можно получить сериализованные данные структуры, но они будут в LE.
Теперь, один раз разбираем структуру и смотрим, какие байты нужно поменять.
сериализованные данные и применяем уже сгенерированные правила к ней.
[StructLayout(LayoutKind.Sequential)]
class Data3
{
public int Id;
public short Date;
[MyPackAttribute(Size=6)]
public string Name;
}
class Packer
{
public void Pack(BinaryWriter writer, object data)
{
foreach (var field in data.GetType.GetFields(..
{
if (field.FieldType == typeof(int
writer.Write(IPAddress.HostToNetworkOrderint)field.GetValue(data;
else if (field.FieldType == typeof(short
writer.Write(IPAddress.HostToNetworkOrdershort)field.GetValue(data;
else if (field.FieldType == typeof(string
{
var s = (string)field.GetValue;
var size = field.Attributes.OfType<MyPackAttribute>.First.Size ? s.Length;
for (var i = 0; i < size; ++i)
{
if (i < s.Length)
writer.Write(s[i]);
else
writer.Writechar)0);
}
}
}
}
}
самое унылое, что для каждого атомарного типа надо добавить свою ветку, но это делается один раз
GetType еще нет, вот GetFields и получение аттрибутов уже да.
поэтому обычно получение GetFields и аттрибутов делается один раз, переводится в свой формат и кэшируется в Dictionary<Type, MyPackFormatInfo>
если нужна очень высокая скорость, то для каждого типа через emit генерится пакер/unpacker и кэшируется уже такой packer.
фишку с MyPackAttribute догнал. круто.
спасибо!
если нужна очень высокая скорость, то для каждого типа через emit генерится пакер/unpвот это наверно я про это и думал. Тут даже предложили просто для каждой стуктуры по паре функций написать.
pack_struct1(.. unpack_struct1(.. pack_struct2(.. unpack_struct2(..).
это окупается если общение будет локальное через shared memory.
как только появляется сеть, то весь наносекундный выигрыш съедают миллисекундные задержки в сети.
> ут даже предложили просто для каждой стуктуры по паре функций написать. pack_struct1(.. unpack_struct1(.. pack_struct2(.. unpack_struct2(..).
один из вариантов, что эти функции генерятся на этапе компиляции, с помощью кода аналогичному вышеприведенному
Сейчас обычно через создание Expression и Compile. особенно если .net 4.0 можно использовать
Вот реально, что пугает в .NET'е (давненько уже ничего не писал на нем так это то, что вот в плюсах тебе что-нибудь надо, ну ты берешь и пишешь, если это не что-то сильно большое или если оно не будет общаться с чем-то внешним (ну там, xml какой-нибудь). А в .NET'е как-то аж не по себе становится от того, что приходится программировать, т.е. кажется, что там все уже реализовано до нас.
Оставить комментарий
Phoenix
Хочу передать стркутуру по сети.что-то вроде
send(sock, &msg, sizeof(msg 0);
Возникает проблема с порядком байт на винде. Хочу чтобы был сетевой порядок порядок(big endian, как я понял который не на винде(там little endian).
(int) 1 -> 00 00 00 01, а не 01 00 00 00.
в с# нарыл такое.
Но и там, и там порядок байт не тот, который нужен.
Что делать?