diff --git a/net/FlatBuffers/ByteBuffer.cs b/net/FlatBuffers/ByteBuffer.cs old mode 100644 new mode 100755 index c406faf35..b591b74b0 --- a/net/FlatBuffers/ByteBuffer.cs +++ b/net/FlatBuffers/ByteBuffer.cs @@ -14,13 +14,18 @@ * limitations under the License. */ +//#define UNSAFE_BYTEBUFFER // uncomment this line to use faster ByteBuffer + using System; using System.Linq; namespace FlatBuffers { /// - /// Class to mimick Java's ByteBuffer which is used heavily in Flatbuffers + /// Class to mimic Java's ByteBuffer which is used heavily in Flatbuffers. + /// If your execution environment allows unsafe code, you should enable + /// unsafe code in your project and #define UNSAFE_BYTEBUFFER to use a + /// MUCH faster version of ByteBuffer. /// public class ByteBuffer { @@ -39,11 +44,38 @@ namespace FlatBuffers public int position() { return _pos; } + // Helper functions for the unsafe version. + static public ushort ReverseBytes(ushort input) + { + return (ushort)(((input & 0x00FFU) << 8) | + ((input & 0xFF00U) >> 8)); + } + static public uint ReverseBytes(uint input) + { + return ((input & 0x000000FFU) << 24) | + ((input & 0x0000FF00U) << 8) | + ((input & 0x00FF0000U) >> 8) | + ((input & 0xFF000000U) >> 24); + } + static public ulong ReverseBytes(ulong input) + { + return (((input & 0x00000000000000FFUL) << 56) | + ((input & 0x000000000000FF00UL) << 40) | + ((input & 0x0000000000FF0000UL) << 24) | + ((input & 0x00000000FF000000UL) << 8) | + ((input & 0x000000FF00000000UL) >> 8) | + ((input & 0x0000FF0000000000UL) >> 24) | + ((input & 0x00FF000000000000UL) >> 40) | + ((input & 0xFF00000000000000UL) >> 56)); + } + +#if !UNSAFE_BYTEBUFFER + // Helper functions for the safe (but slower) version. protected void WriteLittleEndian(int offset, byte[] data) { if (!BitConverter.IsLittleEndian) { - data = data.Reverse().ToArray(); + Array.Reverse(data, 0, data.Length); } Buffer.BlockCopy(data, 0, _buffer, offset, data.Length); _pos = offset; @@ -58,6 +90,7 @@ namespace FlatBuffers ? tmp : tmp.Reverse().ToArray(); } +#endif // !UNSAFE_BYTEBUFFER private void AssertOffsetAndLength(int offset, int length) { @@ -81,6 +114,96 @@ namespace FlatBuffers _pos = offset; } +#if UNSAFE_BYTEBUFFER + // Unsafe but more efficient versions of Put*. + public void PutShort(int offset, short value) + { + PutUshort(offset, (ushort)value); + } + + public unsafe void PutUshort(int offset, ushort value) + { + AssertOffsetAndLength(offset, sizeof(ushort)); + fixed (byte* ptr = _buffer) + { + *(ushort*)(ptr + offset) = BitConverter.IsLittleEndian + ? value + : ReverseBytes(value); + } + _pos = offset; + } + + public void PutInt(int offset, int value) + { + PutUint(offset, (uint)value); + } + + public unsafe void PutUint(int offset, uint value) + { + AssertOffsetAndLength(offset, sizeof(uint)); + fixed (byte* ptr = _buffer) + { + *(uint*)(ptr + offset) = BitConverter.IsLittleEndian + ? value + : ReverseBytes(value); + } + _pos = offset; + } + + public unsafe void PutLong(int offset, long value) + { + PutUlong(offset, (ulong)value); + } + + public unsafe void PutUlong(int offset, ulong value) + { + AssertOffsetAndLength(offset, sizeof(ulong)); + + fixed (byte* ptr = _buffer) + { + *(ulong*)(ptr + offset) = BitConverter.IsLittleEndian + ? value + : ReverseBytes(value); + } + _pos = offset; + } + + public unsafe void PutFloat(int offset, float value) + { + AssertOffsetAndLength(offset, sizeof(float)); + fixed (byte* ptr = _buffer) + { + if (BitConverter.IsLittleEndian) + { + *(float*)(ptr + offset) = value; + } + else + { + *(uint*)(ptr + offset) = ReverseBytes(*(uint*)(&value)); + } + } + _pos = offset; + } + + public unsafe void PutDouble(int offset, double value) + { + AssertOffsetAndLength(offset, sizeof(double)); + fixed (byte* ptr = _buffer) + { + if (BitConverter.IsLittleEndian) + { + *(double*)(ptr + offset) = value; + + } + else + { + *(ulong*)(ptr + offset) = ReverseBytes(*(ulong*)(ptr + offset)); + } + } + _pos = offset; + } +#else // !UNSAFE_BYTEBUFFER + // Slower versions of Put* for when unsafe code is not allowed. public void PutShort(int offset, short value) { AssertOffsetAndLength(offset, sizeof(short)); @@ -129,6 +252,8 @@ namespace FlatBuffers WriteLittleEndian(offset, BitConverter.GetBytes(value)); } +#endif // UNSAFE_BYTEBUFFER + public sbyte GetSbyte(int index) { AssertOffsetAndLength(index, sizeof(sbyte)); @@ -141,6 +266,91 @@ namespace FlatBuffers return _buffer[index]; } +#if UNSAFE_BYTEBUFFER + // Unsafe but more efficient versions of Get*. + public short GetShort(int offset) + { + return (short)GetUshort(offset); + } + + public unsafe ushort GetUshort(int offset) + { + AssertOffsetAndLength(offset, sizeof(ushort)); + fixed (byte* ptr = _buffer) + { + return BitConverter.IsLittleEndian + ? *(ushort*)(ptr + offset) + : ReverseBytes(*(ushort*)(ptr + offset)); + } + } + + public int GetInt(int offset) + { + return (int)GetUint(offset); + } + + public unsafe uint GetUint(int offset) + { + AssertOffsetAndLength(offset, sizeof(uint)); + fixed (byte* ptr = _buffer) + { + return BitConverter.IsLittleEndian + ? *(uint*)(ptr + offset) + : ReverseBytes(*(uint*)(ptr + offset)); + } + } + + public long GetLong(int offset) + { + return (long)GetUlong(offset); + } + + public unsafe ulong GetUlong(int offset) + { + AssertOffsetAndLength(offset, sizeof(ulong)); + fixed (byte* ptr = _buffer) + { + return BitConverter.IsLittleEndian + ? *(ulong*)(ptr + offset) + : ReverseBytes(*(ulong*)(ptr + offset)); + } + } + + public unsafe float GetFloat(int offset) + { + AssertOffsetAndLength(offset, sizeof(float)); + fixed (byte* ptr = _buffer) + { + if (BitConverter.IsLittleEndian) + { + return *(float*)(ptr + offset); + } + else + { + uint uvalue = ReverseBytes(*(uint*)(ptr + offset)); + return *(float*)(&uvalue); + } + } + } + + public unsafe double GetDouble(int offset) + { + AssertOffsetAndLength(offset, sizeof(double)); + fixed (byte* ptr = _buffer) + { + if (BitConverter.IsLittleEndian) + { + return *(double*)(ptr + offset); + } + else + { + ulong uvalue = ReverseBytes(*(ulong*)(ptr + offset)); + return *(double*)(&uvalue); + } + } + } +#else // !UNSAFE_BYTEBUFFER + // Slower versions of Get* for when unsafe code is not allowed. public short GetShort(int index) { var tmp = ReadLittleEndian(index, sizeof(short)); @@ -196,5 +406,6 @@ namespace FlatBuffers var value = BitConverter.ToDouble(tmp, 0); return value; } +#endif // UNSAFE_BYTEBUFFER } } diff --git a/tests/FlatBuffers.Test/ByteBufferTests.cs b/tests/FlatBuffers.Test/ByteBufferTests.cs index b3c1811c9..d5899a968 100644 --- a/tests/FlatBuffers.Test/ByteBufferTests.cs +++ b/tests/FlatBuffers.Test/ByteBufferTests.cs @@ -239,6 +239,34 @@ namespace FlatBuffers.Test var uut = new ByteBuffer(buffer); Assert.Throws(() => uut.GetLong(0)); } + public void ByteBuffer_ReverseBytesUshort() + { + ushort original = (ushort)0x1234U; + ushort reverse = ByteBuffer.ReverseBytes(original); + Assert.AreEqual(0x3412U, reverse); + ushort rereverse = ByteBuffer.ReverseBytes(reverse); + Assert.AreEqual(original, rereverse); + } + + public void ByteBuffer_ReverseBytesUint() + { + uint original = 0x12345678; + uint reverse = ByteBuffer.ReverseBytes(original); + Assert.AreEqual(0x78563412U, reverse); + + uint rereverse = ByteBuffer.ReverseBytes(reverse); + Assert.AreEqual(original, rereverse); + } + + public void ByteBuffer_ReverseBytesUlong() + { + ulong original = 0x1234567890ABCDEFUL; + ulong reverse = ByteBuffer.ReverseBytes(original); + Assert.AreEqual(0xEFCDAB9078563412UL, reverse); + + ulong rereverse = ByteBuffer.ReverseBytes(reverse); + Assert.AreEqual(original, rereverse); + } } }