Home | History | Annotate | Download | only in 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 2 #defines that have an impact on performance 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 // Using UNSAFE_BYTEBUFFER and BYTEBUFFER_NO_BOUNDS_CHECK together can yield a
     28 // performance gain of ~15% for some operations, however doing so is potentially
     29 // dangerous. Do so at your own risk!
     30 //
     31 
     32 using System;
     33 
     34 namespace FlatBuffers
     35 {
     36     /// <summary>
     37     /// Class to mimic Java's ByteBuffer which is used heavily in Flatbuffers.
     38     /// </summary>
     39     public class ByteBuffer
     40     {
     41         private readonly byte[] _buffer;
     42         private int _pos;  // Must track start of the buffer.
     43 
     44         public int Length { get { return _buffer.Length; } }
     45 
     46         public byte[] Data { get { return _buffer; } }
     47 
     48         public ByteBuffer(byte[] buffer) : this(buffer, 0) { }
     49 
     50         public ByteBuffer(byte[] buffer, int pos)
     51         {
     52             _buffer = buffer;
     53             _pos = pos;
     54         }
     55 
     56         public int Position {
     57             get { return _pos; }
     58             set { _pos = value; }
     59         }
     60 
     61         public void Reset()
     62         {
     63             _pos = 0;
     64         }
     65 
     66         // Pre-allocated helper arrays for convertion.
     67         private float[] floathelper = new[] { 0.0f };
     68         private int[] inthelper = new[] { 0 };
     69         private double[] doublehelper = new[] { 0.0 };
     70         private ulong[] ulonghelper = new[] { 0UL };
     71 
     72         // Helper functions for the unsafe version.
     73         static public ushort ReverseBytes(ushort input)
     74         {
     75             return (ushort)(((input & 0x00FFU) << 8) |
     76                             ((input & 0xFF00U) >> 8));
     77         }
     78         static public uint ReverseBytes(uint input)
     79         {
     80             return ((input & 0x000000FFU) << 24) |
     81                    ((input & 0x0000FF00U) <<  8) |
     82                    ((input & 0x00FF0000U) >>  8) |
     83                    ((input & 0xFF000000U) >> 24);
     84         }
     85         static public ulong ReverseBytes(ulong input)
     86         {
     87             return (((input & 0x00000000000000FFUL) << 56) |
     88                     ((input & 0x000000000000FF00UL) << 40) |
     89                     ((input & 0x0000000000FF0000UL) << 24) |
     90                     ((input & 0x00000000FF000000UL) <<  8) |
     91                     ((input & 0x000000FF00000000UL) >>  8) |
     92                     ((input & 0x0000FF0000000000UL) >> 24) |
     93                     ((input & 0x00FF000000000000UL) >> 40) |
     94                     ((input & 0xFF00000000000000UL) >> 56));
     95         }
     96 
     97 #if !UNSAFE_BYTEBUFFER
     98         // Helper functions for the safe (but slower) version.
     99         protected void WriteLittleEndian(int offset, int count, ulong data)
    100         {
    101             if (BitConverter.IsLittleEndian)
    102             {
    103                 for (int i = 0; i < count; i++)
    104                 {
    105                     _buffer[offset + i] = (byte)(data >> i * 8);
    106                 }
    107             }
    108             else
    109             {
    110                 for (int i = 0; i < count; i++)
    111                 {
    112                     _buffer[offset + count - 1 - i] = (byte)(data >> i * 8);
    113                 }
    114             }
    115         }
    116 
    117         protected ulong ReadLittleEndian(int offset, int count)
    118         {
    119             AssertOffsetAndLength(offset, count);
    120             ulong r = 0;
    121             if (BitConverter.IsLittleEndian)
    122             {
    123                 for (int i = 0; i < count; i++)
    124                 {
    125                   r |= (ulong)_buffer[offset + i] << i * 8;
    126                 }
    127             }
    128             else
    129             {
    130               for (int i = 0; i < count; i++)
    131               {
    132                 r |= (ulong)_buffer[offset + count - 1 - i] << i * 8;
    133               }
    134             }
    135             return r;
    136         }
    137 #endif // !UNSAFE_BYTEBUFFER
    138 
    139 
    140         private void AssertOffsetAndLength(int offset, int length)
    141         {
    142             #if !BYTEBUFFER_NO_BOUNDS_CHECK
    143             if (offset < 0 ||
    144                 offset > _buffer.Length - length)
    145                 throw new ArgumentOutOfRangeException();
    146             #endif
    147         }
    148 
    149         public void PutSbyte(int offset, sbyte value)
    150         {
    151             AssertOffsetAndLength(offset, sizeof(sbyte));
    152             _buffer[offset] = (byte)value;
    153         }
    154 
    155         public void PutByte(int offset, byte value)
    156         {
    157             AssertOffsetAndLength(offset, sizeof(byte));
    158             _buffer[offset] = value;
    159         }
    160 
    161         public void PutByte(int offset, byte value, int count)
    162         {
    163             AssertOffsetAndLength(offset, sizeof(byte) * count);
    164             for (var i = 0; i < count; ++i)
    165                 _buffer[offset + i] = value;
    166         }
    167 
    168         // this method exists in order to conform with Java ByteBuffer standards
    169         public void Put(int offset, byte value)
    170         {
    171             PutByte(offset, value);
    172         }
    173 
    174 #if UNSAFE_BYTEBUFFER
    175         // Unsafe but more efficient versions of Put*.
    176         public void PutShort(int offset, short value)
    177         {
    178             PutUshort(offset, (ushort)value);
    179         }
    180 
    181         public unsafe void PutUshort(int offset, ushort value)
    182         {
    183             AssertOffsetAndLength(offset, sizeof(ushort));
    184             fixed (byte* ptr = _buffer)
    185             {
    186                 *(ushort*)(ptr + offset) = BitConverter.IsLittleEndian
    187                     ? value
    188                     : ReverseBytes(value);
    189             }
    190         }
    191 
    192         public void PutInt(int offset, int value)
    193         {
    194             PutUint(offset, (uint)value);
    195         }
    196 
    197         public unsafe void PutUint(int offset, uint value)
    198         {
    199             AssertOffsetAndLength(offset, sizeof(uint));
    200             fixed (byte* ptr = _buffer)
    201             {
    202                 *(uint*)(ptr + offset) = BitConverter.IsLittleEndian
    203                     ? value
    204                     : ReverseBytes(value);
    205             }
    206         }
    207 
    208         public unsafe void PutLong(int offset, long value)
    209         {
    210             PutUlong(offset, (ulong)value);
    211         }
    212 
    213         public unsafe void PutUlong(int offset, ulong value)
    214         {
    215             AssertOffsetAndLength(offset, sizeof(ulong));
    216             fixed (byte* ptr = _buffer)
    217             {
    218                 *(ulong*)(ptr + offset) = BitConverter.IsLittleEndian
    219                     ? value
    220                     : ReverseBytes(value);
    221             }
    222         }
    223 
    224         public unsafe void PutFloat(int offset, float value)
    225         {
    226             AssertOffsetAndLength(offset, sizeof(float));
    227             fixed (byte* ptr = _buffer)
    228             {
    229                 if (BitConverter.IsLittleEndian)
    230                 {
    231                     *(float*)(ptr + offset) = value;
    232                 }
    233                 else
    234                 {
    235                     *(uint*)(ptr + offset) = ReverseBytes(*(uint*)(&value));
    236                 }
    237             }
    238         }
    239 
    240         public unsafe void PutDouble(int offset, double value)
    241         {
    242             AssertOffsetAndLength(offset, sizeof(double));
    243             fixed (byte* ptr = _buffer)
    244             {
    245                 if (BitConverter.IsLittleEndian)
    246                 {
    247                     *(double*)(ptr + offset) = value;
    248 
    249                 }
    250                 else
    251                 {
    252                     *(ulong*)(ptr + offset) = ReverseBytes(*(ulong*)(ptr + offset));
    253                 }
    254             }
    255         }
    256 #else // !UNSAFE_BYTEBUFFER
    257         // Slower versions of Put* for when unsafe code is not allowed.
    258         public void PutShort(int offset, short value)
    259         {
    260             AssertOffsetAndLength(offset, sizeof(short));
    261             WriteLittleEndian(offset, sizeof(short), (ulong)value);
    262         }
    263 
    264         public void PutUshort(int offset, ushort value)
    265         {
    266             AssertOffsetAndLength(offset, sizeof(ushort));
    267             WriteLittleEndian(offset, sizeof(ushort), (ulong)value);
    268         }
    269 
    270         public void PutInt(int offset, int value)
    271         {
    272             AssertOffsetAndLength(offset, sizeof(int));
    273             WriteLittleEndian(offset, sizeof(int), (ulong)value);
    274         }
    275 
    276         public void PutUint(int offset, uint value)
    277         {
    278             AssertOffsetAndLength(offset, sizeof(uint));
    279             WriteLittleEndian(offset, sizeof(uint), (ulong)value);
    280         }
    281 
    282         public void PutLong(int offset, long value)
    283         {
    284             AssertOffsetAndLength(offset, sizeof(long));
    285             WriteLittleEndian(offset, sizeof(long), (ulong)value);
    286         }
    287 
    288         public void PutUlong(int offset, ulong value)
    289         {
    290             AssertOffsetAndLength(offset, sizeof(ulong));
    291             WriteLittleEndian(offset, sizeof(ulong), value);
    292         }
    293 
    294         public void PutFloat(int offset, float value)
    295         {
    296             AssertOffsetAndLength(offset, sizeof(float));
    297             floathelper[0] = value;
    298             Buffer.BlockCopy(floathelper, 0, inthelper, 0, sizeof(float));
    299             WriteLittleEndian(offset, sizeof(float), (ulong)inthelper[0]);
    300         }
    301 
    302         public void PutDouble(int offset, double value)
    303         {
    304             AssertOffsetAndLength(offset, sizeof(double));
    305             doublehelper[0] = value;
    306             Buffer.BlockCopy(doublehelper, 0, ulonghelper, 0, sizeof(double));
    307             WriteLittleEndian(offset, sizeof(double), ulonghelper[0]);
    308         }
    309 
    310 #endif // UNSAFE_BYTEBUFFER
    311 
    312         public sbyte GetSbyte(int index)
    313         {
    314             AssertOffsetAndLength(index, sizeof(sbyte));
    315             return (sbyte)_buffer[index];
    316         }
    317 
    318         public byte Get(int index)
    319         {
    320             AssertOffsetAndLength(index, sizeof(byte));
    321             return _buffer[index];
    322         }
    323 
    324 #if UNSAFE_BYTEBUFFER
    325         // Unsafe but more efficient versions of Get*.
    326         public short GetShort(int offset)
    327         {
    328             return (short)GetUshort(offset);
    329         }
    330 
    331         public unsafe ushort GetUshort(int offset)
    332         {
    333             AssertOffsetAndLength(offset, sizeof(ushort));
    334             fixed (byte* ptr = _buffer)
    335             {
    336                 return BitConverter.IsLittleEndian
    337                     ? *(ushort*)(ptr + offset)
    338                     : ReverseBytes(*(ushort*)(ptr + offset));
    339             }
    340         }
    341 
    342         public int GetInt(int offset)
    343         {
    344             return (int)GetUint(offset);
    345         }
    346 
    347         public unsafe uint GetUint(int offset)
    348         {
    349             AssertOffsetAndLength(offset, sizeof(uint));
    350             fixed (byte* ptr = _buffer)
    351             {
    352                 return BitConverter.IsLittleEndian
    353                     ? *(uint*)(ptr + offset)
    354                     : ReverseBytes(*(uint*)(ptr + offset));
    355             }
    356         }
    357 
    358         public long GetLong(int offset)
    359         {
    360             return (long)GetUlong(offset);
    361         }
    362 
    363         public unsafe ulong GetUlong(int offset)
    364         {
    365             AssertOffsetAndLength(offset, sizeof(ulong));
    366             fixed (byte* ptr = _buffer)
    367             {
    368                 return BitConverter.IsLittleEndian
    369                     ? *(ulong*)(ptr + offset)
    370                     : ReverseBytes(*(ulong*)(ptr + offset));
    371             }
    372         }
    373 
    374         public unsafe float GetFloat(int offset)
    375         {
    376             AssertOffsetAndLength(offset, sizeof(float));
    377             fixed (byte* ptr = _buffer)
    378             {
    379                 if (BitConverter.IsLittleEndian)
    380                 {
    381                     return *(float*)(ptr + offset);
    382                 }
    383                 else
    384                 {
    385                     uint uvalue = ReverseBytes(*(uint*)(ptr + offset));
    386                     return *(float*)(&uvalue);
    387                 }
    388             }
    389         }
    390 
    391         public unsafe double GetDouble(int offset)
    392         {
    393             AssertOffsetAndLength(offset, sizeof(double));
    394             fixed (byte* ptr = _buffer)
    395             {
    396                 if (BitConverter.IsLittleEndian)
    397                 {
    398                     return *(double*)(ptr + offset);
    399                 }
    400                 else
    401                 {
    402                     ulong uvalue = ReverseBytes(*(ulong*)(ptr + offset));
    403                     return *(double*)(&uvalue);
    404                 }
    405             }
    406         }
    407 #else // !UNSAFE_BYTEBUFFER
    408         // Slower versions of Get* for when unsafe code is not allowed.
    409         public short GetShort(int index)
    410         {
    411             return (short)ReadLittleEndian(index, sizeof(short));
    412         }
    413 
    414         public ushort GetUshort(int index)
    415         {
    416             return (ushort)ReadLittleEndian(index, sizeof(ushort));
    417         }
    418 
    419         public int GetInt(int index)
    420         {
    421             return (int)ReadLittleEndian(index, sizeof(int));
    422         }
    423 
    424         public uint GetUint(int index)
    425         {
    426             return (uint)ReadLittleEndian(index, sizeof(uint));
    427         }
    428 
    429         public long GetLong(int index)
    430         {
    431            return (long)ReadLittleEndian(index, sizeof(long));
    432         }
    433 
    434         public ulong GetUlong(int index)
    435         {
    436             return ReadLittleEndian(index, sizeof(ulong));
    437         }
    438 
    439         public float GetFloat(int index)
    440         {
    441             int i = (int)ReadLittleEndian(index, sizeof(float));
    442             inthelper[0] = i;
    443             Buffer.BlockCopy(inthelper, 0, floathelper, 0, sizeof(float));
    444             return floathelper[0];
    445         }
    446 
    447         public double GetDouble(int index)
    448         {
    449             ulong i = ReadLittleEndian(index, sizeof(double));
    450             // There's Int64BitsToDouble but it uses unsafe code internally.
    451             ulonghelper[0] = i;
    452             Buffer.BlockCopy(ulonghelper, 0, doublehelper, 0, sizeof(double));
    453             return doublehelper[0];
    454         }
    455 #endif // UNSAFE_BYTEBUFFER
    456     }
    457 }
    458