Упаковка байтов вручную для отправки по сети - PullRequest
0 голосов
/ 15 мая 2018

У меня есть объект, который имеет следующие переменные:

bool firstBool;  
float firstFloat; (0.0 to 1.0)  
float secondFloat (0.0 to 1.0)  
int firstInt; (0 to 10,000)

Я использовал метод ToString, чтобы получить строку, которую я могу отправить по сети. Расширение Я столкнулся с проблемами с количеством данных, которые это занимает. строка выглядит следующим образом: "false:1.0:1.0:10000" это 19 символов по 2 байта на каждый, так что 38 байтов

Я знаю, что могу сэкономить на этом размере, вручную сохраняя данные в 4 байта следующим образом:

A|B|B|B|B|B|B|B  
C|C|C|C|C|C|C|D  
D|D|D|D|D|D|D|D  
D|D|D|D|D|X|X|X  

A = bool(0 or 1), B = int(0 to 128), C = int(0 to 128), D = int(0 to 16384), X = Leftover bits  
  • Я конвертирую float(0.0 to 1.0) в int(0 to 128), так как могу перестроить их на другом конце, и точность не имеет большого значения.

Я экспериментировал с BitArray и byte [] для преобразования данных в двоичную структуру и из нее.

После некоторых экспериментов я закончил этот процесс сериализации (я знаю, что его нужно очистить и оптимизировать)

public byte[] Serialize() {
    byte[] firstFloatBytes = BitConverter.GetBytes(Mathf.FloorToInt(firstFloat * 128)); //Convert the float to int from (0 to 128)

    byte[] secondFloatBytes = BitConverter.GetBytes(Mathf.FloorToInt(secondFloat * 128)); //Convert the float to int from (0 to 128)

    byte[] firstIntData = BitConverter.GetBytes(Mathf.FloorToInt(firstInt)); // Get the bytes for the int

    BitArray data = new BitArray(32); // create the size 32 bitarray to hold all the data

    int i = 0; // create the index value

    data[i] = firstBool; // set the 0 bit

    BitArray ffBits = new BitArray(firstFloatBytes);
    for(i = 1; i < 8; i++) {
        data[i] = ffBits[i-1]; // Set bits 1 to 7
    }

    BitArray sfBits = new BitArray(secondFloatBytes);
    for(i = 8; i < 15; i++) {
        data[i] = sfBits[i-8]; // Set bits 8 to 14
    }

    BitArray fiBits = new BitArray(firstIntData);
    for(i = 15; i < 29; i++) {
        data[i] = fiBits[i-15]; // Set bits 15 to 28
    }
    byte[] output = new byte[4]; // create a byte[] to hold the output
    data.CopyTo(output,0); // Copy the bits to the byte[]
    return output;
}

Получить информацию из этой структуры намного сложнее, чем получить ее в этой форме. Я полагаю, что, возможно, смогу что-то тренировать, используя побитовые операторы и битовые маски.

Это оказывается сложнее, чем я ожидал. Я думал, что было бы очень легко получить доступ к битам байта [], чтобы напрямую манипулировать данными, извлекать диапазоны битов, а затем преобразовывать обратно в значения, необходимые для перестройки объекта. Существуют ли лучшие практики для этого типа сериализации данных? Кто-нибудь знает учебник или пример ссылки, которую я мог бы прочитать?

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

Вот простой способ упаковки / распаковки. Но вы теряете точность при преобразовании числа с плавающей запятой только в 7/8 бит

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (Data data in Data.input)
            {
                Data.Print(data);
                Data results = Data.Unpack(Data.Pack(data));
                Data.Print(results);
            }
            Console.ReadLine();
        }
    }
    public class Data
    {
        public static List<Data> input = new List<Data>() {
            new Data() { firstBool = true, firstFloat = 0.2345F, secondFloat = 0.432F,   firstInt = 12},
            new Data() { firstBool = true, firstFloat = 0.3445F, secondFloat = 0.432F,   firstInt = 11},
            new Data() { firstBool = false, firstFloat = 0.2365F, secondFloat = 0.432F,   firstInt = 9},
            new Data() { firstBool = false, firstFloat = 0.545F, secondFloat = 0.432F,   firstInt = 8},
            new Data() { firstBool = true, firstFloat = 0.2367F, secondFloat = 0.432F,   firstInt = 7}
        };


        public bool firstBool { get; set; }
        public float firstFloat {get; set; } //(0.0 to 1.0)  
        public float secondFloat {get; set; } //(0.0 to 1.0)  
        public int firstInt { get; set; } //(0 to 10,000)

        public static byte[] Pack(Data data)
        {
            byte[] results = new byte[4];

            results[0] = (byte)((data.firstBool ? 0x80 : 0x00) | (byte)(data.firstFloat * 128));
            results[1] = (byte)(data.secondFloat * 256);
            results[2] = (byte)((data.firstInt >> 8) & 0xFF);
            results[3] = (byte)(data.firstInt & 0xFF);

            return results;
        }
        public static Data Unpack(byte[] data)
        {
            Data results = new Data();

            results.firstBool = ((data[0] & 0x80) == 0) ? false : true;
            results.firstFloat = ((float)(data[0] & 0x7F)) / 128.0F;
            results.secondFloat = (float)data[1] / 256.0F;
            results.firstInt = (data[2] << 8) | data[3];

            return results;
        }
        public static void Print(Data data)
        {
            Console.WriteLine("Bool : '{0}', 1st Float : '{1}', 2nd Float : '{2}', Int : '{3}'",
                data.firstBool,
                data.firstFloat,
                data.secondFloat,
                data.firstInt
                );


        }
    }
}
0 голосов
/ 16 мая 2018

Стандартные и эффективные методы сериализации:

  1. Использование BinaryWriter / BinaryReader:

    public byte[] Serialize()
    {
       using(var s = new MemoryStream())
       using(var w = new BinaryWriter(s))
       {
          w.Write(firstBool);
          w.Write(firstFloat);
          ...
          return s.ToArray();
       }
    }
    
    public void Deserialize(byte[] bytes)
    {
       using(var s = new MemoryStream(bytes))
       using(var r = new BinaryReader(s))
       {
          firstBool = r.ReadBool();
          firstFload = r.ReadFloat();
          ...
       }
    }
    
  2. Использование protobuf.net

BinaryWriter / BinaryReader намного быстрее (примерно в 7 раз). Protobuf является более гибким, простым в использовании, очень популярным и сериализуется примерно на 33% меньше байтов. (конечно, эти числа являются порядками и зависят от того, что вы сериализуете и как).

Теперь в основном BinaryWriter напишет 1 + 4 + 4 + 4 = 13 байтов. Вы сокращаете его до 5 байт, сначала конвертируя значения в bool, byte, byte, short, округляя его так, как вам нужно. Наконец, легко объединить bool с одним из ваших байтов, чтобы получить 4 байта, если вы действительно этого хотите.

Я на самом деле не против ручной сериализации Но это должно стоить цену с точки зрения производительности. Код довольно нечитаемый. Используйте битовые маски и двоичные сдвиги непосредственно в байтах, но сохраняйте это как можно более простым. Не используйте BitArray. Это медленно и не более читабельно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...