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