...

Text file src/github.com/google/flatbuffers/net/FlatBuffers/ByteBuffer.cs

Documentation: github.com/google/flatbuffers/net/FlatBuffers

     1/*
     2 * Copyright 2014 Google Inc. All rights reserved.
     3 *
     4 * Licensed under the Apache License, Version 2.0 (the "License");
     5 * you may not use this file except in compliance with the License.
     6 * You may obtain a copy of the License at
     7 *
     8 *     http://www.apache.org/licenses/LICENSE-2.0
     9 *
    10 * Unless required by applicable law or agreed to in writing, software
    11 * distributed under the License is distributed on an "AS IS" BASIS,
    12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13 * See the License for the specific language governing permissions and
    14 * limitations under the License.
    15 */
    16
    17// There are three conditional compilation symbols that have an impact on performance/features of this ByteBuffer implementation.
    18//
    19//      UNSAFE_BYTEBUFFER
    20//          This will use unsafe code to manipulate the underlying byte array. This
    21//          can yield a reasonable performance increase.
    22//
    23//      BYTEBUFFER_NO_BOUNDS_CHECK
    24//          This will disable the bounds check asserts to the byte array. This can
    25//          yield a small performance gain in normal code.
    26//
    27//      ENABLE_SPAN_T
    28//          This will enable reading and writing blocks of memory with a Span<T> instead of just
    29//          T[].  You can also enable writing directly to shared memory or other types of memory
    30//          by providing a custom implementation of ByteBufferAllocator.
    31//          ENABLE_SPAN_T also requires UNSAFE_BYTEBUFFER to be defined, or .NET
    32//          Standard 2.1.
    33//
    34// Using UNSAFE_BYTEBUFFER and BYTEBUFFER_NO_BOUNDS_CHECK together can yield a
    35// performance gain of ~15% for some operations, however doing so is potentially
    36// dangerous. Do so at your own risk!
    37//
    38
    39using System;
    40using System.Collections.Generic;
    41using System.IO;
    42using System.Runtime.CompilerServices;
    43using System.Runtime.InteropServices;
    44using System.Text;
    45
    46#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
    47using System.Buffers.Binary;
    48#endif
    49
    50#if ENABLE_SPAN_T && !UNSAFE_BYTEBUFFER && !NETSTANDARD2_1
    51#warning ENABLE_SPAN_T requires UNSAFE_BYTEBUFFER to also be defined
    52#endif
    53
    54namespace Google.FlatBuffers
    55{
    56    public abstract class ByteBufferAllocator
    57    {
    58#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
    59        public abstract Span<byte> Span { get; }
    60        public abstract ReadOnlySpan<byte> ReadOnlySpan { get; }
    61        public abstract Memory<byte> Memory { get; }
    62        public abstract ReadOnlyMemory<byte> ReadOnlyMemory { get; }
    63
    64#else
    65        public byte[] Buffer
    66        {
    67            get;
    68            protected set;
    69        }
    70#endif
    71
    72        public int Length
    73        {
    74            get;
    75            protected set;
    76        }
    77
    78        public abstract void GrowFront(int newSize);
    79    }
    80
    81    public sealed class ByteArrayAllocator : ByteBufferAllocator
    82    {
    83        private byte[] _buffer;
    84
    85        public ByteArrayAllocator(byte[] buffer)
    86        {
    87            _buffer = buffer;
    88            InitBuffer();
    89        }
    90
    91        public override void GrowFront(int newSize)
    92        {
    93            if ((Length & 0xC0000000) != 0)
    94                throw new Exception(
    95                    "ByteBuffer: cannot grow buffer beyond 2 gigabytes.");
    96
    97            if (newSize < Length)
    98                throw new Exception("ByteBuffer: cannot truncate buffer.");
    99
   100            byte[] newBuffer = new byte[newSize];
   101            System.Buffer.BlockCopy(_buffer, 0, newBuffer, newSize - Length, Length);
   102            _buffer = newBuffer;
   103            InitBuffer();
   104        }
   105
   106#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
   107        public override Span<byte> Span => _buffer;
   108        public override ReadOnlySpan<byte> ReadOnlySpan => _buffer;
   109        public override Memory<byte> Memory => _buffer;
   110        public override ReadOnlyMemory<byte> ReadOnlyMemory => _buffer;
   111#endif
   112
   113        private void InitBuffer()
   114        {
   115            Length = _buffer.Length;
   116#if !ENABLE_SPAN_T
   117            Buffer = _buffer;
   118#endif
   119        }
   120    }
   121
   122    /// <summary>
   123    /// Class to mimic Java's ByteBuffer which is used heavily in Flatbuffers.
   124    /// </summary>
   125    public class ByteBuffer
   126    {
   127        private ByteBufferAllocator _buffer;
   128        private int _pos;  // Must track start of the buffer.
   129
   130        public ByteBuffer(ByteBufferAllocator allocator, int position)
   131        {
   132            _buffer = allocator;
   133            _pos = position;
   134        }
   135
   136        public ByteBuffer(int size) : this(new byte[size]) { }
   137
   138        public ByteBuffer(byte[] buffer) : this(buffer, 0) { }
   139
   140        public ByteBuffer(byte[] buffer, int pos)
   141        {
   142            _buffer = new ByteArrayAllocator(buffer);
   143            _pos = pos;
   144        }
   145
   146        public int Position
   147        {
   148            get { return _pos; }
   149            set { _pos = value; }
   150        }
   151
   152        public int Length { get { return _buffer.Length; } }
   153
   154        public void Reset()
   155        {
   156            _pos = 0;
   157        }
   158
   159        // Create a new ByteBuffer on the same underlying data.
   160        // The new ByteBuffer's position will be same as this buffer's.
   161        public ByteBuffer Duplicate()
   162        {
   163            return new ByteBuffer(_buffer, Position);
   164        }
   165
   166        // Increases the size of the ByteBuffer, and copies the old data towards
   167        // the end of the new buffer.
   168        public void GrowFront(int newSize)
   169        {
   170            _buffer.GrowFront(newSize);
   171        }
   172
   173        public byte[] ToArray(int pos, int len)
   174        {
   175            return ToArray<byte>(pos, len);
   176        }
   177
   178        /// <summary>
   179        /// A lookup of type sizes. Used instead of Marshal.SizeOf() which has additional
   180        /// overhead, but also is compatible with generic functions for simplified code.
   181        /// </summary>
   182        private static Dictionary<Type, int> genericSizes = new Dictionary<Type, int>()
   183        {
   184            { typeof(bool),     sizeof(bool) },
   185            { typeof(float),    sizeof(float) },
   186            { typeof(double),   sizeof(double) },
   187            { typeof(sbyte),    sizeof(sbyte) },
   188            { typeof(byte),     sizeof(byte) },
   189            { typeof(short),    sizeof(short) },
   190            { typeof(ushort),   sizeof(ushort) },
   191            { typeof(int),      sizeof(int) },
   192            { typeof(uint),     sizeof(uint) },
   193            { typeof(ulong),    sizeof(ulong) },
   194            { typeof(long),     sizeof(long) },
   195        };
   196
   197        /// <summary>
   198        /// Get the wire-size (in bytes) of a type supported by flatbuffers.
   199        /// </summary>
   200        /// <param name="t">The type to get the wire size of</param>
   201        /// <returns></returns>
   202        public static int SizeOf<T>()
   203        {
   204            return genericSizes[typeof(T)];
   205        }
   206
   207        /// <summary>
   208        /// Checks if the Type provided is supported as scalar value
   209        /// </summary>
   210        /// <typeparam name="T">The Type to check</typeparam>
   211        /// <returns>True if the type is a scalar type that is supported, falsed otherwise</returns>
   212        public static bool IsSupportedType<T>()
   213        {
   214            return genericSizes.ContainsKey(typeof(T));
   215        }
   216
   217        /// <summary>
   218        /// Get the wire-size (in bytes) of an typed array
   219        /// </summary>
   220        /// <typeparam name="T">The type of the array</typeparam>
   221        /// <param name="x">The array to get the size of</param>
   222        /// <returns>The number of bytes the array takes on wire</returns>
   223        public static int ArraySize<T>(T[] x)
   224        {
   225            return SizeOf<T>() * x.Length;
   226        }
   227
   228        /// <summary>
   229        /// Get the wire-size (in bytes) of an typed array segment, taking only the
   230        /// range specified by <paramref name="x"/> into account.
   231        /// </summary>
   232        /// <typeparam name="T">The type of the array</typeparam>
   233        /// <param name="x">The array segment to get the size of</param>
   234        /// <returns>The number of bytes the array segment takes on wire</returns>
   235        public static int ArraySize<T>(ArraySegment<T> x)
   236        {
   237            return SizeOf<T>() * x.Count;
   238        }
   239
   240#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
   241        public static int ArraySize<T>(Span<T> x)
   242        {
   243            return SizeOf<T>() * x.Length;
   244        }
   245#endif
   246
   247        // Get a portion of the buffer casted into an array of type T, given
   248        // the buffer position and length.
   249#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
   250        public T[] ToArray<T>(int pos, int len)
   251            where T : struct
   252        {
   253            AssertOffsetAndLength(pos, len);
   254            return MemoryMarshal.Cast<byte, T>(_buffer.ReadOnlySpan.Slice(pos)).Slice(0, len).ToArray();
   255        }
   256#else
   257        public T[] ToArray<T>(int pos, int len)
   258            where T : struct
   259        {
   260            AssertOffsetAndLength(pos, len);
   261            T[] arr = new T[len];
   262            Buffer.BlockCopy(_buffer.Buffer, pos, arr, 0, ArraySize(arr));
   263            return arr;
   264        }
   265#endif
   266
   267        public byte[] ToSizedArray()
   268        {
   269            return ToArray<byte>(Position, Length - Position);
   270        }
   271
   272        public byte[] ToFullArray()
   273        {
   274            return ToArray<byte>(0, Length);
   275        }
   276
   277#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
   278        public ReadOnlyMemory<byte> ToReadOnlyMemory(int pos, int len)
   279        {
   280            return _buffer.ReadOnlyMemory.Slice(pos, len);
   281        }
   282
   283        public Memory<byte> ToMemory(int pos, int len)
   284        {
   285            return _buffer.Memory.Slice(pos, len);
   286        }
   287
   288        public Span<byte> ToSpan(int pos, int len)
   289        {
   290            return _buffer.Span.Slice(pos, len);
   291        }
   292#else
   293        public ArraySegment<byte> ToArraySegment(int pos, int len)
   294        {
   295            return new ArraySegment<byte>(_buffer.Buffer, pos, len);
   296        }
   297
   298        public MemoryStream ToMemoryStream(int pos, int len)
   299        {
   300            return new MemoryStream(_buffer.Buffer, pos, len);
   301        }
   302#endif
   303
   304#if !UNSAFE_BYTEBUFFER
   305        // A conversion union where all the members are overlapping. This allows to reinterpret the bytes of one type
   306        // as another, without additional copies.
   307        [StructLayout(LayoutKind.Explicit)]
   308        struct ConversionUnion
   309        {
   310          [FieldOffset(0)] public int intValue;
   311          [FieldOffset(0)] public float floatValue;
   312        }
   313#endif // !UNSAFE_BYTEBUFFER
   314
   315        // Helper functions for the unsafe version.
   316        static public ushort ReverseBytes(ushort input)
   317        {
   318            return (ushort)(((input & 0x00FFU) << 8) |
   319                            ((input & 0xFF00U) >> 8));
   320        }
   321        static public uint ReverseBytes(uint input)
   322        {
   323            return ((input & 0x000000FFU) << 24) |
   324                   ((input & 0x0000FF00U) <<  8) |
   325                   ((input & 0x00FF0000U) >>  8) |
   326                   ((input & 0xFF000000U) >> 24);
   327        }
   328        static public ulong ReverseBytes(ulong input)
   329        {
   330            return (((input & 0x00000000000000FFUL) << 56) |
   331                    ((input & 0x000000000000FF00UL) << 40) |
   332                    ((input & 0x0000000000FF0000UL) << 24) |
   333                    ((input & 0x00000000FF000000UL) <<  8) |
   334                    ((input & 0x000000FF00000000UL) >>  8) |
   335                    ((input & 0x0000FF0000000000UL) >> 24) |
   336                    ((input & 0x00FF000000000000UL) >> 40) |
   337                    ((input & 0xFF00000000000000UL) >> 56));
   338        }
   339
   340#if !UNSAFE_BYTEBUFFER && (!ENABLE_SPAN_T || !NETSTANDARD2_1)
   341        // Helper functions for the safe (but slower) version.
   342        protected void WriteLittleEndian(int offset, int count, ulong data)
   343        {
   344            if (BitConverter.IsLittleEndian)
   345            {
   346                for (int i = 0; i < count; i++)
   347                {
   348                    _buffer.Buffer[offset + i] = (byte)(data >> i * 8);
   349                }
   350            }
   351            else
   352            {
   353                for (int i = 0; i < count; i++)
   354                {
   355                    _buffer.Buffer[offset + count - 1 - i] = (byte)(data >> i * 8);
   356                }
   357            }
   358        }
   359
   360        protected ulong ReadLittleEndian(int offset, int count)
   361        {
   362            AssertOffsetAndLength(offset, count);
   363            ulong r = 0;
   364            if (BitConverter.IsLittleEndian)
   365            {
   366                for (int i = 0; i < count; i++)
   367                {
   368                    r |= (ulong)_buffer.Buffer[offset + i] << i * 8;
   369                }
   370            }
   371            else
   372            {
   373                for (int i = 0; i < count; i++)
   374                {
   375                    r |= (ulong)_buffer.Buffer[offset + count - 1 - i] << i * 8;
   376                }
   377            }
   378            return r;
   379        }
   380#elif ENABLE_SPAN_T && NETSTANDARD2_1
   381        protected void WriteLittleEndian(int offset, int count, ulong data)
   382        {
   383            if (BitConverter.IsLittleEndian)
   384            {
   385                for (int i = 0; i < count; i++)
   386                {
   387                    _buffer.Span[offset + i] = (byte)(data >> i * 8);
   388                }
   389            }
   390            else
   391            {
   392                for (int i = 0; i < count; i++)
   393                {
   394                    _buffer.Span[offset + count - 1 - i] = (byte)(data >> i * 8);
   395                }
   396            }
   397        }
   398
   399        protected ulong ReadLittleEndian(int offset, int count)
   400        {
   401            AssertOffsetAndLength(offset, count);
   402            ulong r = 0;
   403            if (BitConverter.IsLittleEndian)
   404            {
   405                for (int i = 0; i < count; i++)
   406                {
   407                    r |= (ulong)_buffer.Span[offset + i] << i * 8;
   408                }
   409            }
   410            else
   411            {
   412                for (int i = 0; i < count; i++)
   413                {
   414                    r |= (ulong)_buffer.Span[offset + count - 1 - i] << i * 8;
   415                }
   416            }
   417            return r;
   418        }
   419#endif
   420
   421        private void AssertOffsetAndLength(int offset, int length)
   422        {
   423#if !BYTEBUFFER_NO_BOUNDS_CHECK
   424            if (offset < 0 ||
   425                offset > _buffer.Length - length)
   426                throw new ArgumentOutOfRangeException();
   427#endif
   428        }
   429
   430#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
   431
   432        public void PutSbyte(int offset, sbyte value)
   433        {
   434            AssertOffsetAndLength(offset, sizeof(sbyte));
   435            _buffer.Span[offset] = (byte)value;
   436        }
   437
   438        public void PutByte(int offset, byte value)
   439        {
   440            AssertOffsetAndLength(offset, sizeof(byte));
   441            _buffer.Span[offset] = value;
   442        }
   443
   444        public void PutByte(int offset, byte value, int count)
   445        {
   446            AssertOffsetAndLength(offset, sizeof(byte) * count);
   447            Span<byte> span = _buffer.Span.Slice(offset, count);
   448            for (var i = 0; i < span.Length; ++i)
   449                span[i] = value;
   450        }
   451#else
   452        public void PutSbyte(int offset, sbyte value)
   453        {
   454            AssertOffsetAndLength(offset, sizeof(sbyte));
   455            _buffer.Buffer[offset] = (byte)value;
   456        }
   457
   458        public void PutByte(int offset, byte value)
   459        {
   460            AssertOffsetAndLength(offset, sizeof(byte));
   461            _buffer.Buffer[offset] = value;
   462        }
   463
   464        public void PutByte(int offset, byte value, int count)
   465        {
   466            AssertOffsetAndLength(offset, sizeof(byte) * count);
   467            for (var i = 0; i < count; ++i)
   468                _buffer.Buffer[offset + i] = value;
   469        }
   470#endif
   471
   472        // this method exists in order to conform with Java ByteBuffer standards
   473        public void Put(int offset, byte value)
   474        {
   475            PutByte(offset, value);
   476        }
   477
   478#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
   479        public unsafe void PutStringUTF8(int offset, string value)
   480        {
   481            AssertOffsetAndLength(offset, value.Length);
   482            fixed (char* s = value)
   483            {
   484                fixed (byte* buffer = &MemoryMarshal.GetReference(_buffer.Span))
   485                {
   486                    Encoding.UTF8.GetBytes(s, value.Length, buffer + offset, Length - offset);
   487                }
   488            }
   489        }
   490#elif ENABLE_SPAN_T && NETSTANDARD2_1
   491        public void PutStringUTF8(int offset, string value)
   492        {
   493            AssertOffsetAndLength(offset, value.Length);
   494            Encoding.UTF8.GetBytes(value.AsSpan().Slice(0, value.Length),
   495                _buffer.Span.Slice(offset));
   496        }
   497#else
   498        public void PutStringUTF8(int offset, string value)
   499        {
   500            AssertOffsetAndLength(offset, value.Length);
   501            Encoding.UTF8.GetBytes(value, 0, value.Length,
   502                _buffer.Buffer, offset);
   503        }
   504#endif
   505
   506#if UNSAFE_BYTEBUFFER
   507        // Unsafe but more efficient versions of Put*.
   508        public void PutShort(int offset, short value)
   509        {
   510            PutUshort(offset, (ushort)value);
   511        }
   512
   513        public unsafe void PutUshort(int offset, ushort value)
   514        {
   515            AssertOffsetAndLength(offset, sizeof(ushort));
   516#if ENABLE_SPAN_T // && UNSAFE_BYTEBUFFER
   517            Span<byte> span = _buffer.Span.Slice(offset);
   518            BinaryPrimitives.WriteUInt16LittleEndian(span, value);
   519#else
   520            fixed (byte* ptr = _buffer.Buffer)
   521            {
   522                *(ushort*)(ptr + offset) = BitConverter.IsLittleEndian
   523                    ? value
   524                    : ReverseBytes(value);
   525            }
   526#endif
   527        }
   528
   529        public void PutInt(int offset, int value)
   530        {
   531            PutUint(offset, (uint)value);
   532        }
   533
   534        public unsafe void PutUint(int offset, uint value)
   535        {
   536            AssertOffsetAndLength(offset, sizeof(uint));
   537#if ENABLE_SPAN_T // && UNSAFE_BYTEBUFFER
   538            Span<byte> span = _buffer.Span.Slice(offset);
   539            BinaryPrimitives.WriteUInt32LittleEndian(span, value);
   540#else
   541            fixed (byte* ptr = _buffer.Buffer)
   542            {
   543                *(uint*)(ptr + offset) = BitConverter.IsLittleEndian
   544                    ? value
   545                    : ReverseBytes(value);
   546            }
   547#endif
   548        }
   549
   550        public unsafe void PutLong(int offset, long value)
   551        {
   552            PutUlong(offset, (ulong)value);
   553        }
   554
   555        public unsafe void PutUlong(int offset, ulong value)
   556        {
   557            AssertOffsetAndLength(offset, sizeof(ulong));
   558#if ENABLE_SPAN_T // && UNSAFE_BYTEBUFFER
   559            Span<byte> span = _buffer.Span.Slice(offset);
   560            BinaryPrimitives.WriteUInt64LittleEndian(span, value);
   561#else
   562            fixed (byte* ptr = _buffer.Buffer)
   563            {
   564                *(ulong*)(ptr + offset) = BitConverter.IsLittleEndian
   565                    ? value
   566                    : ReverseBytes(value);
   567            }
   568#endif
   569        }
   570
   571        public unsafe void PutFloat(int offset, float value)
   572        {
   573            AssertOffsetAndLength(offset, sizeof(float));
   574#if ENABLE_SPAN_T // && UNSAFE_BYTEBUFFER
   575            fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.Span))
   576#else
   577            fixed (byte* ptr = _buffer.Buffer)
   578#endif
   579            {
   580                if (BitConverter.IsLittleEndian)
   581                {
   582                    *(float*)(ptr + offset) = value;
   583                }
   584                else
   585                {
   586                    *(uint*)(ptr + offset) = ReverseBytes(*(uint*)(&value));
   587                }
   588            }
   589        }
   590
   591        public unsafe void PutDouble(int offset, double value)
   592        {
   593            AssertOffsetAndLength(offset, sizeof(double));
   594#if ENABLE_SPAN_T // && UNSAFE_BYTEBUFFER
   595            fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.Span))
   596#else
   597            fixed (byte* ptr = _buffer.Buffer)
   598#endif
   599            {
   600                if (BitConverter.IsLittleEndian)
   601                {
   602                    *(double*)(ptr + offset) = value;
   603                }
   604                else
   605                {
   606                    *(ulong*)(ptr + offset) = ReverseBytes(*(ulong*)(&value));
   607                }
   608            }
   609        }
   610#else // !UNSAFE_BYTEBUFFER
   611        // Slower versions of Put* for when unsafe code is not allowed.
   612        public void PutShort(int offset, short value)
   613        {
   614            AssertOffsetAndLength(offset, sizeof(short));
   615            WriteLittleEndian(offset, sizeof(short), (ulong)value);
   616        }
   617
   618        public void PutUshort(int offset, ushort value)
   619        {
   620            AssertOffsetAndLength(offset, sizeof(ushort));
   621            WriteLittleEndian(offset, sizeof(ushort), (ulong)value);
   622        }
   623
   624        public void PutInt(int offset, int value)
   625        {
   626            AssertOffsetAndLength(offset, sizeof(int));
   627            WriteLittleEndian(offset, sizeof(int), (ulong)value);
   628        }
   629
   630        public void PutUint(int offset, uint value)
   631        {
   632            AssertOffsetAndLength(offset, sizeof(uint));
   633            WriteLittleEndian(offset, sizeof(uint), (ulong)value);
   634        }
   635
   636        public void PutLong(int offset, long value)
   637        {
   638            AssertOffsetAndLength(offset, sizeof(long));
   639            WriteLittleEndian(offset, sizeof(long), (ulong)value);
   640        }
   641
   642        public void PutUlong(int offset, ulong value)
   643        {
   644            AssertOffsetAndLength(offset, sizeof(ulong));
   645            WriteLittleEndian(offset, sizeof(ulong), value);
   646        }
   647
   648        public void PutFloat(int offset, float value)
   649        {
   650            AssertOffsetAndLength(offset, sizeof(float));
   651            // TODO(derekbailey): use BitConvert.SingleToInt32Bits() whenever flatbuffers upgrades to a .NET version
   652            // that contains it.
   653            ConversionUnion union;
   654            union.intValue = 0;
   655            union.floatValue = value;    
   656            WriteLittleEndian(offset, sizeof(float), (ulong)union.intValue);
   657        }
   658
   659        public void PutDouble(int offset, double value)
   660        {
   661            AssertOffsetAndLength(offset, sizeof(double));
   662            WriteLittleEndian(offset, sizeof(double), (ulong)BitConverter.DoubleToInt64Bits(value));
   663        }
   664
   665#endif // UNSAFE_BYTEBUFFER
   666
   667#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
   668        public sbyte GetSbyte(int index)
   669        {
   670            AssertOffsetAndLength(index, sizeof(sbyte));
   671            return (sbyte)_buffer.ReadOnlySpan[index];
   672        }
   673
   674        public byte Get(int index)
   675        {
   676            AssertOffsetAndLength(index, sizeof(byte));
   677            return _buffer.ReadOnlySpan[index];
   678        }
   679#else
   680        public sbyte GetSbyte(int index)
   681        {
   682            AssertOffsetAndLength(index, sizeof(sbyte));
   683            return (sbyte)_buffer.Buffer[index];
   684        }
   685
   686        public byte Get(int index)
   687        {
   688            AssertOffsetAndLength(index, sizeof(byte));
   689            return _buffer.Buffer[index];
   690        }
   691#endif
   692
   693#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
   694        public unsafe string GetStringUTF8(int startPos, int len)
   695        {
   696            fixed (byte* buffer = &MemoryMarshal.GetReference(_buffer.ReadOnlySpan.Slice(startPos)))
   697            {
   698                return Encoding.UTF8.GetString(buffer, len);
   699            }
   700        }
   701#elif ENABLE_SPAN_T && NETSTANDARD2_1
   702        public string GetStringUTF8(int startPos, int len)
   703        {
   704            return Encoding.UTF8.GetString(_buffer.Span.Slice(startPos, len));
   705        }
   706#else
   707        public string GetStringUTF8(int startPos, int len)
   708        {
   709            return Encoding.UTF8.GetString(_buffer.Buffer, startPos, len);
   710        }
   711#endif
   712
   713#if UNSAFE_BYTEBUFFER
   714        // Unsafe but more efficient versions of Get*.
   715        public short GetShort(int offset)
   716        {
   717            return (short)GetUshort(offset);
   718        }
   719
   720        public unsafe ushort GetUshort(int offset)
   721        {
   722            AssertOffsetAndLength(offset, sizeof(ushort));
   723#if ENABLE_SPAN_T // && UNSAFE_BYTEBUFFER
   724            ReadOnlySpan<byte> span = _buffer.ReadOnlySpan.Slice(offset);
   725            return BinaryPrimitives.ReadUInt16LittleEndian(span);
   726#else
   727            fixed (byte* ptr = _buffer.Buffer)
   728            {
   729                return BitConverter.IsLittleEndian
   730                    ? *(ushort*)(ptr + offset)
   731                    : ReverseBytes(*(ushort*)(ptr + offset));
   732            }
   733#endif
   734        }
   735
   736        public int GetInt(int offset)
   737        {
   738            return (int)GetUint(offset);
   739        }
   740
   741        public unsafe uint GetUint(int offset)
   742        {
   743            AssertOffsetAndLength(offset, sizeof(uint));
   744#if ENABLE_SPAN_T // && UNSAFE_BYTEBUFFER
   745            ReadOnlySpan<byte> span = _buffer.ReadOnlySpan.Slice(offset);
   746            return BinaryPrimitives.ReadUInt32LittleEndian(span);
   747#else
   748            fixed (byte* ptr = _buffer.Buffer)
   749            {
   750                return BitConverter.IsLittleEndian
   751                    ? *(uint*)(ptr + offset)
   752                    : ReverseBytes(*(uint*)(ptr + offset));
   753            }
   754#endif
   755        }
   756
   757        public long GetLong(int offset)
   758        {
   759            return (long)GetUlong(offset);
   760        }
   761
   762        public unsafe ulong GetUlong(int offset)
   763        {
   764            AssertOffsetAndLength(offset, sizeof(ulong));
   765#if ENABLE_SPAN_T // && UNSAFE_BYTEBUFFER
   766            ReadOnlySpan<byte> span = _buffer.ReadOnlySpan.Slice(offset);
   767            return BinaryPrimitives.ReadUInt64LittleEndian(span);
   768#else            
   769            fixed (byte* ptr = _buffer.Buffer)
   770            {
   771                return BitConverter.IsLittleEndian
   772                    ? *(ulong*)(ptr + offset)
   773                    : ReverseBytes(*(ulong*)(ptr + offset));
   774            }
   775#endif
   776        }
   777
   778        public unsafe float GetFloat(int offset)
   779        {
   780            AssertOffsetAndLength(offset, sizeof(float));
   781#if ENABLE_SPAN_T // && UNSAFE_BYTEBUFFER
   782            fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.ReadOnlySpan))
   783#else
   784            fixed (byte* ptr = _buffer.Buffer)
   785#endif
   786            {
   787                if (BitConverter.IsLittleEndian)
   788                {
   789                    return *(float*)(ptr + offset);
   790                }
   791                else
   792                {
   793                    uint uvalue = ReverseBytes(*(uint*)(ptr + offset));
   794                    return *(float*)(&uvalue);
   795                }
   796            }
   797        }
   798
   799        public unsafe double GetDouble(int offset)
   800        {
   801            AssertOffsetAndLength(offset, sizeof(double));
   802#if ENABLE_SPAN_T // && UNSAFE_BYTEBUFFER
   803            fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.ReadOnlySpan))
   804#else
   805            fixed (byte* ptr = _buffer.Buffer)
   806#endif
   807            {
   808                if (BitConverter.IsLittleEndian)
   809                {
   810                    return *(double*)(ptr + offset);
   811                }
   812                else
   813                {
   814                    ulong uvalue = ReverseBytes(*(ulong*)(ptr + offset));
   815                    return *(double*)(&uvalue);
   816                }
   817            }
   818        }
   819#else // !UNSAFE_BYTEBUFFER
   820        // Slower versions of Get* for when unsafe code is not allowed.
   821        public short GetShort(int index)
   822        {
   823            return (short)ReadLittleEndian(index, sizeof(short));
   824        }
   825
   826        public ushort GetUshort(int index)
   827        {
   828            return (ushort)ReadLittleEndian(index, sizeof(ushort));
   829        }
   830
   831        public int GetInt(int index)
   832        {
   833            return (int)ReadLittleEndian(index, sizeof(int));
   834        }
   835
   836        public uint GetUint(int index)
   837        {
   838            return (uint)ReadLittleEndian(index, sizeof(uint));
   839        }
   840
   841        public long GetLong(int index)
   842        {
   843            return (long)ReadLittleEndian(index, sizeof(long));
   844        }
   845
   846        public ulong GetUlong(int index)
   847        {
   848            return ReadLittleEndian(index, sizeof(ulong));
   849        }
   850
   851        public float GetFloat(int index)
   852        {
   853            // TODO(derekbailey): use BitConvert.Int32BitsToSingle() whenever flatbuffers upgrades to a .NET version
   854            // that contains it.
   855            ConversionUnion union;
   856            union.floatValue = 0;
   857            union.intValue = (int)ReadLittleEndian(index, sizeof(float));
   858            return union.floatValue;
   859        }
   860
   861        public double GetDouble(int index)
   862        {
   863            return BitConverter.Int64BitsToDouble((long)ReadLittleEndian(index, sizeof(double)));
   864        }
   865#endif // UNSAFE_BYTEBUFFER
   866
   867        /// <summary>
   868        /// Copies an array of type T into this buffer, ending at the given
   869        /// offset into this buffer. The starting offset is calculated based on the length
   870        /// of the array and is the value returned.
   871        /// </summary>
   872        /// <typeparam name="T">The type of the input data (must be a struct)</typeparam>
   873        /// <param name="offset">The offset into this buffer where the copy will end</param>
   874        /// <param name="x">The array to copy data from</param>
   875        /// <returns>The 'start' location of this buffer now, after the copy completed</returns>
   876        public int Put<T>(int offset, T[] x)
   877            where T : struct
   878        {
   879            if (x == null)
   880            {
   881                throw new ArgumentNullException("Cannot put a null array");
   882            }
   883
   884            return Put(offset, new ArraySegment<T>(x));
   885        }
   886
   887        /// <summary>
   888        /// Copies an array segment of type T into this buffer, ending at the 
   889        /// given offset into this buffer. The starting offset is calculated 
   890        /// based on the count of the array segment and is the value returned.
   891        /// </summary>
   892        /// <typeparam name="T">The type of the input data (must be a struct)
   893        /// </typeparam>
   894        /// <param name="offset">The offset into this buffer where the copy 
   895        /// will end</param>
   896        /// <param name="x">The array segment to copy data from</param>
   897        /// <returns>The 'start' location of this buffer now, after the copy 
   898        /// completed</returns>
   899        public int Put<T>(int offset, ArraySegment<T> x)
   900            where T : struct
   901        {
   902            if (x.Equals(default(ArraySegment<T>)))
   903            {
   904                throw new ArgumentNullException("Cannot put a uninitialized array segment");
   905            }
   906
   907            if (x.Count == 0)
   908            {
   909                throw new ArgumentException("Cannot put an empty array");
   910            }
   911
   912            if (!IsSupportedType<T>())
   913            {
   914                throw new ArgumentException("Cannot put an array of type "
   915                    + typeof(T) + " into this buffer");
   916            }
   917
   918            if (BitConverter.IsLittleEndian)
   919            {
   920                int numBytes = ByteBuffer.ArraySize(x);
   921                offset -= numBytes;
   922                AssertOffsetAndLength(offset, numBytes);
   923                // if we are LE, just do a block copy
   924#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
   925                MemoryMarshal.Cast<T, byte>(x).CopyTo(_buffer.Span.Slice(offset, numBytes));
   926#else
   927                var srcOffset = ByteBuffer.SizeOf<T>() * x.Offset;
   928                Buffer.BlockCopy(x.Array, srcOffset, _buffer.Buffer, offset, numBytes);
   929#endif
   930            }
   931            else
   932            {
   933                throw new NotImplementedException("Big Endian Support not implemented yet " +
   934                    "for putting typed arrays");
   935                // if we are BE, we have to swap each element by itself
   936                //for(int i = x.Length - 1; i >= 0; i--)
   937                //{
   938                //  todo: low priority, but need to genericize the Put<T>() functions
   939                //}
   940            }
   941            return offset;
   942        }
   943
   944        /// <summary>
   945        /// Copies an array segment of type T into this buffer, ending at the 
   946        /// given offset into this buffer. The starting offset is calculated 
   947        /// based on the count of the array segment and is the value returned.
   948        /// </summary>
   949        /// <typeparam name="T">The type of the input data (must be a struct)
   950        /// </typeparam>
   951        /// <param name="offset">The offset into this buffer where the copy 
   952        /// will end</param>
   953        /// <param name="ptr">The pointer to copy data from</param>
   954        /// <param name="sizeInBytes">The number of bytes to copy</param>
   955        /// <returns>The 'start' location of this buffer now, after the copy 
   956        /// completed</returns>
   957        public int Put<T>(int offset, IntPtr ptr, int sizeInBytes)
   958            where T : struct
   959        {
   960            if (ptr == IntPtr.Zero)
   961            {
   962                throw new ArgumentNullException("Cannot add a null pointer");
   963            }
   964
   965            if(sizeInBytes <= 0)
   966            {
   967                throw new ArgumentException("Cannot put an empty array");
   968            }
   969
   970            if (!IsSupportedType<T>())
   971            {
   972                throw new ArgumentException("Cannot put an array of type "
   973                    + typeof(T) + " into this buffer");
   974            }
   975
   976            if (BitConverter.IsLittleEndian)
   977            {
   978                offset -= sizeInBytes;
   979                AssertOffsetAndLength(offset, sizeInBytes);
   980                // if we are LE, just do a block copy
   981#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
   982                unsafe
   983                { 
   984                    var span = new Span<byte>(ptr.ToPointer(), sizeInBytes);
   985                    span.CopyTo(_buffer.Span.Slice(offset, sizeInBytes));
   986                }
   987#else
   988                Marshal.Copy(ptr, _buffer.Buffer, offset, sizeInBytes);
   989#endif
   990            }
   991            else
   992            {
   993                throw new NotImplementedException("Big Endian Support not implemented yet " +
   994                    "for putting typed arrays");
   995                // if we are BE, we have to swap each element by itself
   996                //for(int i = x.Length - 1; i >= 0; i--)
   997                //{
   998                //  todo: low priority, but need to genericize the Put<T>() functions
   999                //}
  1000            }
  1001            return offset;
  1002        }
  1003
  1004#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
  1005        public int Put<T>(int offset, Span<T> x)
  1006            where T : struct
  1007        {
  1008            if (x.Length == 0)
  1009            {
  1010                throw new ArgumentException("Cannot put an empty array");
  1011            }
  1012
  1013            if (!IsSupportedType<T>())
  1014            {
  1015                throw new ArgumentException("Cannot put an array of type "
  1016                    + typeof(T) + " into this buffer");
  1017            }
  1018
  1019            if (BitConverter.IsLittleEndian)
  1020            {
  1021                int numBytes = ByteBuffer.ArraySize(x);
  1022                offset -= numBytes;
  1023                AssertOffsetAndLength(offset, numBytes);
  1024                // if we are LE, just do a block copy
  1025                MemoryMarshal.Cast<T, byte>(x).CopyTo(_buffer.Span.Slice(offset, numBytes));
  1026            }
  1027            else
  1028            {
  1029                throw new NotImplementedException("Big Endian Support not implemented yet " +
  1030                    "for putting typed arrays");
  1031                // if we are BE, we have to swap each element by itself
  1032                //for(int i = x.Length - 1; i >= 0; i--)
  1033                //{
  1034                //  todo: low priority, but need to genericize the Put<T>() functions
  1035                //}
  1036            }
  1037            return offset;
  1038        }
  1039#endif
  1040    }
  1041}

View as plain text