[c#] Как изменить порядок байт при сериализации структуры LE-__m_GT

Phoenix

Хочу передать стркутуру по сети.
что-то вроде
send(sock, &msg, sizeof(msg 0);
Возникает проблема с порядком байт на винде. Хочу чтобы был сетевой порядок порядок(big endian, как я понял который не на винде(там little endian).
(int) 1 -> 00 00 00 01, а не 01 00 00 00.
в с# нарыл такое.
Но и там, и там порядок байт не тот, который нужен.

[StructLayout(LayoutKind.Explicit,Pack = 1, CharSet = CharSet.Ansi)]
public struct Data2
{
[FieldOffset(0)]
public int ID;
[FieldOffset(4)]
public short Date;
//public string Text;
}


static public byte[] ConverDataToByteArray2(object anything)

{
int rawsize = Marshal.SizeOf(anything);
IntPtr buffer = Marshal.AllocHGlobal(rawsize);
Marshal.StructureToPtr(anything, buffer, false);

byte[] rawdata = new byte[rawsize];

Marshal.Copy(buffer, rawdata, 0, rawsize);
Marshal.FreeHGlobal(buffer);
return rawdata;
}


Что делать?

Phoenix

вот тут советуют руками всё преобразовывать http://stackoverflow.com/questions/3492304/c-encounter-a-pro...
Неужели нет чего-нибудь типа питоновской библиотерки 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'
>>>

Phoenix

где-то в инете нарыл вот такое
        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;
}

Dasar

что-то извратное
один раз пишутся функции pack/unpack, которые используя reflection, ipaddress.NetworkToHostOrder/HostToNetworkOrder, binaryreader/writer - распаковывают или запаковывают структуры в последовательность байт

Phoenix

ок. понял.
вопрос такой.
есть структура

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 не было проверок на соответсвие типов, приведений и т.д.
Я не силён в терминологии, но надеюсь, что мысль изложил доступно.

Phoenix

как сдвинуть строку, я нашёл. Как-то всё очень извращённо получается.
 
    
[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];

}

Dasar

забудь про marshal и соответственно про аттрибуты FieldOffset, StructLayout, fixed и т.д.
сделай свой pack/unpack через reflection

Phoenix

всё. понял, о чём ты.
про unsafe выше - это я не туда полез, да.

Dasar

для своего пакера из штатных нужен только аттрибут StructLayout с LayoutKind.Sequential, который вешается на класс.
это необходимо, чтобы поля не перемешивались, когда их получаешь через x.GetType.GetFields

Phoenix

Погоди, а как в структуре задать фиксированную длину byte[] ?

Phoenix

GetType тормознуто работает же.
я хочу, чтобы в pack /unpack эти функции не вызывались.
У меня есть идея, но она тянет на какой-то жёсткий хак.

можно получить сериализованные данные структуры, но они будут в LE.
Теперь, один раз разбираем структуру и смотрим, какие байты нужно поменять.
сериализованные данные и применяем уже сгенерированные правила к ней.

Dasar


[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);
}
}
}
}
}

самое унылое, что для каждого атомарного типа надо добавить свою ветку, но это делается один раз

Dasar

>GetType тормознуто работает же.
GetType еще нет, вот GetFields и получение аттрибутов уже да.
поэтому обычно получение GetFields и аттрибутов делается один раз, переводится в свой формат и кэшируется в Dictionary<Type, MyPackFormatInfo>
если нужна очень высокая скорость, то для каждого типа через emit генерится пакер/unpacker и кэшируется уже такой packer.

Phoenix

ух ты. готовое решение :o
фишку с MyPackAttribute догнал. круто.
спасибо!

Phoenix

если нужна очень высокая скорость, то для каждого типа через emit генерится пакер/unp
вот это наверно я про это и думал. Тут даже предложили просто для каждой стуктуры по паре функций написать.
pack_struct1(.. unpack_struct1(.. pack_struct2(.. unpack_struct2(..).

Dasar

>вот это наверно я про это и думал. Тут даже предложили просто для каждой стуктуры по паре функций написать.
это окупается если общение будет локальное через shared memory.
как только появляется сеть, то весь наносекундный выигрыш съедают миллисекундные задержки в сети.
> ут даже предложили просто для каждой стуктуры по паре функций написать. pack_struct1(.. unpack_struct1(.. pack_struct2(.. unpack_struct2(..).
один из вариантов, что эти функции генерятся на этапе компиляции, с помощью кода аналогичному вышеприведенному

Dasar

Сейчас обычно через создание Expression и Compile. особенно если .net 4.0 можно использовать

Serab

Вот реально, что пугает в .NET'е (давненько уже ничего не писал на нем так это то, что вот в плюсах тебе что-нибудь надо, ну ты берешь и пишешь, если это не что-то сильно большое или если оно не будет общаться с чем-то внешним (ну там, xml какой-нибудь). А в .NET'е как-то аж не по себе становится от того, что приходится программировать, т.е. кажется, что там все уже реализовано до нас.
Оставить комментарий
Имя или ник:
Комментарий: