• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ByteBuffer(byte[] buffer)48         public ByteBuffer(byte[] buffer) : this(buffer, 0) { }
49 
ByteBuffer(byte[] buffer, int pos)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 
Reset()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.
ReverseBytes(ushort input)73         static public ushort ReverseBytes(ushort input)
74         {
75             return (ushort)(((input & 0x00FFU) << 8) |
76                             ((input & 0xFF00U) >> 8));
77         }
ReverseBytes(uint input)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         }
ReverseBytes(ulong input)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.
WriteLittleEndian(int offset, int count, ulong data)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 
ReadLittleEndian(int offset, int count)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 
AssertOffsetAndLength(int offset, int length)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 
PutSbyte(int offset, sbyte value)149         public void PutSbyte(int offset, sbyte value)
150         {
151             AssertOffsetAndLength(offset, sizeof(sbyte));
152             _buffer[offset] = (byte)value;
153         }
154 
PutByte(int offset, byte value)155         public void PutByte(int offset, byte value)
156         {
157             AssertOffsetAndLength(offset, sizeof(byte));
158             _buffer[offset] = value;
159         }
160 
PutByte(int offset, byte value, int count)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
Put(int offset, byte value)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*.
PutShort(int offset, short value)176         public void PutShort(int offset, short value)
177         {
178             PutUshort(offset, (ushort)value);
179         }
180 
PutUshort(int offset, ushort value)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 
PutInt(int offset, int value)192         public void PutInt(int offset, int value)
193         {
194             PutUint(offset, (uint)value);
195         }
196 
PutUint(int offset, uint value)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 
PutLong(int offset, long value)208         public unsafe void PutLong(int offset, long value)
209         {
210             PutUlong(offset, (ulong)value);
211         }
212 
PutUlong(int offset, ulong value)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 
PutFloat(int offset, float value)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 
PutDouble(int offset, double value)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.
PutShort(int offset, short value)258         public void PutShort(int offset, short value)
259         {
260             AssertOffsetAndLength(offset, sizeof(short));
261             WriteLittleEndian(offset, sizeof(short), (ulong)value);
262         }
263 
PutUshort(int offset, ushort value)264         public void PutUshort(int offset, ushort value)
265         {
266             AssertOffsetAndLength(offset, sizeof(ushort));
267             WriteLittleEndian(offset, sizeof(ushort), (ulong)value);
268         }
269 
PutInt(int offset, int value)270         public void PutInt(int offset, int value)
271         {
272             AssertOffsetAndLength(offset, sizeof(int));
273             WriteLittleEndian(offset, sizeof(int), (ulong)value);
274         }
275 
PutUint(int offset, uint value)276         public void PutUint(int offset, uint value)
277         {
278             AssertOffsetAndLength(offset, sizeof(uint));
279             WriteLittleEndian(offset, sizeof(uint), (ulong)value);
280         }
281 
PutLong(int offset, long value)282         public void PutLong(int offset, long value)
283         {
284             AssertOffsetAndLength(offset, sizeof(long));
285             WriteLittleEndian(offset, sizeof(long), (ulong)value);
286         }
287 
PutUlong(int offset, ulong value)288         public void PutUlong(int offset, ulong value)
289         {
290             AssertOffsetAndLength(offset, sizeof(ulong));
291             WriteLittleEndian(offset, sizeof(ulong), value);
292         }
293 
PutFloat(int offset, float value)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 
PutDouble(int offset, double value)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 
GetSbyte(int index)312         public sbyte GetSbyte(int index)
313         {
314             AssertOffsetAndLength(index, sizeof(sbyte));
315             return (sbyte)_buffer[index];
316         }
317 
Get(int index)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*.
GetShort(int offset)326         public short GetShort(int offset)
327         {
328             return (short)GetUshort(offset);
329         }
330 
GetUshort(int offset)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 
GetInt(int offset)342         public int GetInt(int offset)
343         {
344             return (int)GetUint(offset);
345         }
346 
GetUint(int offset)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 
GetLong(int offset)358         public long GetLong(int offset)
359         {
360             return (long)GetUlong(offset);
361         }
362 
GetUlong(int offset)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 
GetFloat(int offset)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 
GetDouble(int offset)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.
GetShort(int index)409         public short GetShort(int index)
410         {
411             return (short)ReadLittleEndian(index, sizeof(short));
412         }
413 
GetUshort(int index)414         public ushort GetUshort(int index)
415         {
416             return (ushort)ReadLittleEndian(index, sizeof(ushort));
417         }
418 
GetInt(int index)419         public int GetInt(int index)
420         {
421             return (int)ReadLittleEndian(index, sizeof(int));
422         }
423 
GetUint(int index)424         public uint GetUint(int index)
425         {
426             return (uint)ReadLittleEndian(index, sizeof(uint));
427         }
428 
GetLong(int index)429         public long GetLong(int index)
430         {
431            return (long)ReadLittleEndian(index, sizeof(long));
432         }
433 
GetUlong(int index)434         public ulong GetUlong(int index)
435         {
436             return ReadLittleEndian(index, sizeof(ulong));
437         }
438 
GetFloat(int index)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 
GetDouble(int index)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