简体   繁体   中英

Best way to write bytes to a byte array in a struct?

I'm trying to create a packet struct that is basically a byte builder of fixed length. I have a WriteByte function written in 3 different ways. Just wondering which is best (or if there's a better way altogether) and which will keep the GC happy. BTW, I have a Position field that has to be updated when a byte(s) is written. The functions will expand to include WriteUInt16, WriteFloat etc... Not sure what the best approach is. Any advice is appreciated.

  1. Should this be a struct? I'd like to do as little allocation as possible because these Packs will be created very frequently.
  2. Should I put the WriteByte in the struct itself as a method (option 1), as an extension of Pack (passed by ref) (option 2), or just use a static helper class (passed by ref) (option 3)?

Here's the code:

public struct Pack
{
    public byte[] Data { get; internal set; }
    public int Pos { get; internal set; }
    
    public Pack(byte opcode, ushort size)
    {
        ++size;                     // make room for byte opcode
        Data = new byte[2 + size];  // make room to prepend the size (ushort)
        BitConverter.GetBytes(size).CopyTo(Data, 0);
        Data[2] = opcode;
        Pos = 3;                    // start writing at position 3
    }

    // option 1
    // use: pack.WriteByte(0x01)
    public void WriteByte(byte value) => Data[Pos++] = value;
}

public static class SPackExtensions
{
    // option 2
    // use: pack.WriteByte(0x01)
    public static void WriteByte(ref this Pack pack, byte value)
    {
        pack.Data[pack.Pos++] = value;
    }
}

public static class PackWriter
{
    // option 3
    // use: PackWriter.WriteByte(ref pack, 0x01)
    public static void WriteByte(ref Pack pack, byte value)
    {
        pack.Data[pack.Pos++] = value;
    }
}

Should this be a struct? I'd like to do as little allocation as possible because these Packs will be created very frequently.

Maybe. Even with a struct you will still be allocating two objects, one when converting the size to an array, and one for the array itself. So the overhead for allocating the object will probably be negligible, especially since it will probably be much smaller than the array. However, structs are recommended to be immutable , since it can be quite confusing when passing around a object if the data-array is shared, but the Pos is not.

But you should probably use BitConverter.TryWriteBytes or BinaryPrimitives.TryWriteInt16LittleEndian to avoid the allocation when writing the length.

Should I put the WriteByte in the struct itself as a method (option 1), as an extension of Pack (passed by ref) (option 2), or just use a static helper class (passed by ref) (option 3)?

The generated code should be identical, so go for whatever is easiest to use and most readable. I would probably argue for option 1. In my opinion, using extension methods or helper classes would bring little advantages in this case.

these Packs will be created very frequently

When writing performance optimized code, a good principle is avoiding allocations altogether. And as far as I can see this object would be impossible to put into a object pool and reuse, since the position parameter is not possible to reset. So I would consider basing my object around a span that is fetched from a pool of fixed size buffers.

Also, I would highly recommend that you profile your code. "Very frequently" might range from a 1hz to 10^9hz depending on context. So you should check if this is an actual problem in your particular context.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM