1

I have a struct:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct FieldIndex {
   public fixed byte Meta[16];
}

The first byte is TypeCode. The remaining 15 bytes is a UTF8-encoded string.

I'm using the following to fill the nameBuf byte array:

  private static string GetMetaName (FieldIndex meta) {
     var idx = 0;
     var bytesOut = 1;
     var nameBuf = new byte[Indexer.MAXFIELDNAMELENGTH];   // 15

     while (bytesOut < Indexer.MAXFIELDLENGTH) {           // 16
        nameBuf[idx] = *(byte*)((byte*)&meta + bytesOut);

        ++bytesOut;
        ++idx;
     }

     //var src = (byte*)&field.meta + 1;
     //fixed (byte* b = nameBuf) {
     //   *((byte*)b) = (byte)src;
     //}

     return BinaryConverter.ToString(nameBuf, 0, Indexer.MAXFIELDNAMELENGTH, Indexer.DefaultEncoding);
  }

In the commented code above, I was trying to accomplish the same task without the while iteration but it does not work (no compile error, just the wrong interpolation). Can I assign nameBuf without the while-loop?

Update
I'd also prefer using the (byte*) rather than Marshal.Copy.

IAbstract
  • 19,551
  • 15
  • 98
  • 146

2 Answers2

1

You could try to use the Marshal.Copy static method in unsafe context:

unsafe
{
    Marshal.Copy(new IntPtr(meta), nameBuf, 1, nameBuf.Length);
}
Dmitry
  • 13,797
  • 6
  • 32
  • 48
  • Doesn't `Marshal.Copy` just hide the `while` loop (essentially)? – IAbstract Oct 02 '15 at 16:04
  • Never mind that question. I looked at the implementation and it calls an `extern` method (probably C) that most likely resembles what Mono does here: https://github.com/mono/mono/blob/88dc6c2f5207d3d48d7a20fb6bda73fd51d9af2d/mono/metadata/marshal.c#L10922 I was looking for a way to use the `byte* + offset` without `Marshal`ling as well. I'll update my question to see if I can get that. – IAbstract Oct 02 '15 at 16:34
  • `Marshal.Copy` is actually causing an exception: out of bounds. The implementation was giving a compile error as you wrote it, so I changed it up a bit: `Marshal.Copy(new IntPtr((byte*)&meta), nameBuf, 1, nameBuf.Length)` – IAbstract Oct 02 '15 at 17:27
1

There isn't much point in doing this the hard unsafe way when you can do it just easily this way:

[StructLayout(LayoutKind.Sequential)]
struct FieldIndex {
   public byte Typecode;
   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)]
   private byte[] Utf8string;
   public string GetString() {
      return Encoding.UTF8.GetString(Utf8string, 0, 15);
   }
}

No while-loop, mission accomplished. That pointer is still there and is just as efficient, you just don't see it anymore. If you fret over calling the GetString() method too often then simply declare another public class and keep this one internal.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536