blob: 5e212ddd052ec8390d839e4a52d01beaf7660a26 [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001/*
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 3 #defines 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 if 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
32//
33// Using UNSAFE_BYTEBUFFER and BYTEBUFFER_NO_BOUNDS_CHECK together can yield a
34// performance gain of ~15% for some operations, however doing so is potentially
35// dangerous. Do so at your own risk!
36//
37
38using System;
39using System.Collections.Generic;
40using System.IO;
41using System.Runtime.CompilerServices;
42using System.Runtime.InteropServices;
43using System.Text;
44
45#if ENABLE_SPAN_T
46using System.Buffers.Binary;
47#endif
48
49#if ENABLE_SPAN_T && !UNSAFE_BYTEBUFFER
50#error ENABLE_SPAN_T requires UNSAFE_BYTEBUFFER to also be defined
51#endif
52
53namespace FlatBuffers
54{
55 public abstract class ByteBufferAllocator
56 {
57#if ENABLE_SPAN_T
58 public abstract Span<byte> Span { get; }
59 public abstract ReadOnlySpan<byte> ReadOnlySpan { get; }
60 public abstract Memory<byte> Memory { get; }
61 public abstract ReadOnlyMemory<byte> ReadOnlyMemory { get; }
62
63#else
64 public byte[] Buffer
65 {
66 get;
67 protected set;
68 }
69#endif
70
71 public int Length
72 {
73 get;
74 protected set;
75 }
76
77 public abstract void GrowFront(int newSize);
78 }
79
80 public sealed class ByteArrayAllocator : ByteBufferAllocator
81 {
82 private byte[] _buffer;
83
84 public ByteArrayAllocator(byte[] buffer)
85 {
86 _buffer = buffer;
87 InitBuffer();
88 }
89
90 public override void GrowFront(int newSize)
91 {
92 if ((Length & 0xC0000000) != 0)
93 throw new Exception(
94 "ByteBuffer: cannot grow buffer beyond 2 gigabytes.");
95
96 if (newSize < Length)
97 throw new Exception("ByteBuffer: cannot truncate buffer.");
98
99 byte[] newBuffer = new byte[newSize];
100 System.Buffer.BlockCopy(_buffer, 0, newBuffer, newSize - Length, Length);
101 _buffer = newBuffer;
102 InitBuffer();
103 }
104
105#if ENABLE_SPAN_T
106 public override Span<byte> Span => _buffer;
107 public override ReadOnlySpan<byte> ReadOnlySpan => _buffer;
108 public override Memory<byte> Memory => _buffer;
109 public override ReadOnlyMemory<byte> ReadOnlyMemory => _buffer;
110#endif
111
112 private void InitBuffer()
113 {
114 Length = _buffer.Length;
115#if !ENABLE_SPAN_T
116 Buffer = _buffer;
117#endif
118 }
119 }
120
121 /// <summary>
122 /// Class to mimic Java's ByteBuffer which is used heavily in Flatbuffers.
123 /// </summary>
124 public class ByteBuffer
125 {
126 private ByteBufferAllocator _buffer;
127 private int _pos; // Must track start of the buffer.
128
129 public ByteBuffer(ByteBufferAllocator allocator, int position)
130 {
131 _buffer = allocator;
132 _pos = position;
133 }
134
135 public ByteBuffer(int size) : this(new byte[size]) { }
136
137 public ByteBuffer(byte[] buffer) : this(buffer, 0) { }
138
139 public ByteBuffer(byte[] buffer, int pos)
140 {
141 _buffer = new ByteArrayAllocator(buffer);
142 _pos = pos;
143 }
144
145 public int Position
146 {
147 get { return _pos; }
148 set { _pos = value; }
149 }
150
151 public int Length { get { return _buffer.Length; } }
152
153 public void Reset()
154 {
155 _pos = 0;
156 }
157
158 // Create a new ByteBuffer on the same underlying data.
159 // The new ByteBuffer's position will be same as this buffer's.
160 public ByteBuffer Duplicate()
161 {
162 return new ByteBuffer(_buffer, Position);
163 }
164
165 // Increases the size of the ByteBuffer, and copies the old data towards
166 // the end of the new buffer.
167 public void GrowFront(int newSize)
168 {
169 _buffer.GrowFront(newSize);
170 }
171
172 public byte[] ToArray(int pos, int len)
173 {
174 return ToArray<byte>(pos, len);
175 }
176
177 /// <summary>
178 /// A lookup of type sizes. Used instead of Marshal.SizeOf() which has additional
179 /// overhead, but also is compatible with generic functions for simplified code.
180 /// </summary>
181 private static Dictionary<Type, int> genericSizes = new Dictionary<Type, int>()
182 {
183 { typeof(bool), sizeof(bool) },
184 { typeof(float), sizeof(float) },
185 { typeof(double), sizeof(double) },
186 { typeof(sbyte), sizeof(sbyte) },
187 { typeof(byte), sizeof(byte) },
188 { typeof(short), sizeof(short) },
189 { typeof(ushort), sizeof(ushort) },
190 { typeof(int), sizeof(int) },
191 { typeof(uint), sizeof(uint) },
192 { typeof(ulong), sizeof(ulong) },
193 { typeof(long), sizeof(long) },
194 };
195
196 /// <summary>
197 /// Get the wire-size (in bytes) of a type supported by flatbuffers.
198 /// </summary>
199 /// <param name="t">The type to get the wire size of</param>
200 /// <returns></returns>
201 public static int SizeOf<T>()
202 {
203 return genericSizes[typeof(T)];
204 }
205
206 /// <summary>
207 /// Checks if the Type provided is supported as scalar value
208 /// </summary>
209 /// <typeparam name="T">The Type to check</typeparam>
210 /// <returns>True if the type is a scalar type that is supported, falsed otherwise</returns>
211 public static bool IsSupportedType<T>()
212 {
213 return genericSizes.ContainsKey(typeof(T));
214 }
215
216 /// <summary>
217 /// Get the wire-size (in bytes) of an typed array
218 /// </summary>
219 /// <typeparam name="T">The type of the array</typeparam>
220 /// <param name="x">The array to get the size of</param>
221 /// <returns>The number of bytes the array takes on wire</returns>
222 public static int ArraySize<T>(T[] x)
223 {
224 return SizeOf<T>() * x.Length;
225 }
226
227#if ENABLE_SPAN_T
228 public static int ArraySize<T>(Span<T> x)
229 {
230 return SizeOf<T>() * x.Length;
231 }
232#endif
233
234 // Get a portion of the buffer casted into an array of type T, given
235 // the buffer position and length.
236#if ENABLE_SPAN_T
237 public T[] ToArray<T>(int pos, int len)
238 where T : struct
239 {
240 AssertOffsetAndLength(pos, len);
241 return MemoryMarshal.Cast<byte, T>(_buffer.ReadOnlySpan.Slice(pos)).Slice(0, len).ToArray();
242 }
243#else
244 public T[] ToArray<T>(int pos, int len)
245 where T : struct
246 {
247 AssertOffsetAndLength(pos, len);
248 T[] arr = new T[len];
249 Buffer.BlockCopy(_buffer.Buffer, pos, arr, 0, ArraySize(arr));
250 return arr;
251 }
252#endif
253
254 public byte[] ToSizedArray()
255 {
256 return ToArray<byte>(Position, Length - Position);
257 }
258
259 public byte[] ToFullArray()
260 {
261 return ToArray<byte>(0, Length);
262 }
263
264#if ENABLE_SPAN_T
265 public ReadOnlyMemory<byte> ToReadOnlyMemory(int pos, int len)
266 {
267 return _buffer.ReadOnlyMemory.Slice(pos, len);
268 }
269
270 public Memory<byte> ToMemory(int pos, int len)
271 {
272 return _buffer.Memory.Slice(pos, len);
273 }
274
275 public Span<byte> ToSpan(int pos, int len)
276 {
277 return _buffer.Span.Slice(pos, len);
278 }
279#else
280 public ArraySegment<byte> ToArraySegment(int pos, int len)
281 {
282 return new ArraySegment<byte>(_buffer.Buffer, pos, len);
283 }
284
285 public MemoryStream ToMemoryStream(int pos, int len)
286 {
287 return new MemoryStream(_buffer.Buffer, pos, len);
288 }
289#endif
290
291#if !UNSAFE_BYTEBUFFER
292 // Pre-allocated helper arrays for convertion.
293 private float[] floathelper = new[] { 0.0f };
294 private int[] inthelper = new[] { 0 };
295 private double[] doublehelper = new[] { 0.0 };
296 private ulong[] ulonghelper = new[] { 0UL };
297#endif // !UNSAFE_BYTEBUFFER
298
299 // Helper functions for the unsafe version.
300 static public ushort ReverseBytes(ushort input)
301 {
302 return (ushort)(((input & 0x00FFU) << 8) |
303 ((input & 0xFF00U) >> 8));
304 }
305 static public uint ReverseBytes(uint input)
306 {
307 return ((input & 0x000000FFU) << 24) |
308 ((input & 0x0000FF00U) << 8) |
309 ((input & 0x00FF0000U) >> 8) |
310 ((input & 0xFF000000U) >> 24);
311 }
312 static public ulong ReverseBytes(ulong input)
313 {
314 return (((input & 0x00000000000000FFUL) << 56) |
315 ((input & 0x000000000000FF00UL) << 40) |
316 ((input & 0x0000000000FF0000UL) << 24) |
317 ((input & 0x00000000FF000000UL) << 8) |
318 ((input & 0x000000FF00000000UL) >> 8) |
319 ((input & 0x0000FF0000000000UL) >> 24) |
320 ((input & 0x00FF000000000000UL) >> 40) |
321 ((input & 0xFF00000000000000UL) >> 56));
322 }
323
324#if !UNSAFE_BYTEBUFFER
325 // Helper functions for the safe (but slower) version.
326 protected void WriteLittleEndian(int offset, int count, ulong data)
327 {
328 if (BitConverter.IsLittleEndian)
329 {
330 for (int i = 0; i < count; i++)
331 {
332 _buffer.Buffer[offset + i] = (byte)(data >> i * 8);
333 }
334 }
335 else
336 {
337 for (int i = 0; i < count; i++)
338 {
339 _buffer.Buffer[offset + count - 1 - i] = (byte)(data >> i * 8);
340 }
341 }
342 }
343
344 protected ulong ReadLittleEndian(int offset, int count)
345 {
346 AssertOffsetAndLength(offset, count);
347 ulong r = 0;
348 if (BitConverter.IsLittleEndian)
349 {
350 for (int i = 0; i < count; i++)
351 {
352 r |= (ulong)_buffer.Buffer[offset + i] << i * 8;
353 }
354 }
355 else
356 {
357 for (int i = 0; i < count; i++)
358 {
359 r |= (ulong)_buffer.Buffer[offset + count - 1 - i] << i * 8;
360 }
361 }
362 return r;
363 }
364#endif // !UNSAFE_BYTEBUFFER
365
366 private void AssertOffsetAndLength(int offset, int length)
367 {
368#if !BYTEBUFFER_NO_BOUNDS_CHECK
369 if (offset < 0 ||
370 offset > _buffer.Length - length)
371 throw new ArgumentOutOfRangeException();
372#endif
373 }
374
375#if ENABLE_SPAN_T
376
377 public void PutSbyte(int offset, sbyte value)
378 {
379 AssertOffsetAndLength(offset, sizeof(sbyte));
380 _buffer.Span[offset] = (byte)value;
381 }
382
383 public void PutByte(int offset, byte value)
384 {
385 AssertOffsetAndLength(offset, sizeof(byte));
386 _buffer.Span[offset] = value;
387 }
388
389 public void PutByte(int offset, byte value, int count)
390 {
391 AssertOffsetAndLength(offset, sizeof(byte) * count);
392 Span<byte> span = _buffer.Span.Slice(offset, count);
393 for (var i = 0; i < span.Length; ++i)
394 span[i] = value;
395 }
396#else
397 public void PutSbyte(int offset, sbyte value)
398 {
399 AssertOffsetAndLength(offset, sizeof(sbyte));
400 _buffer.Buffer[offset] = (byte)value;
401 }
402
403 public void PutByte(int offset, byte value)
404 {
405 AssertOffsetAndLength(offset, sizeof(byte));
406 _buffer.Buffer[offset] = value;
407 }
408
409 public void PutByte(int offset, byte value, int count)
410 {
411 AssertOffsetAndLength(offset, sizeof(byte) * count);
412 for (var i = 0; i < count; ++i)
413 _buffer.Buffer[offset + i] = value;
414 }
415#endif
416
417 // this method exists in order to conform with Java ByteBuffer standards
418 public void Put(int offset, byte value)
419 {
420 PutByte(offset, value);
421 }
422
423#if ENABLE_SPAN_T
424 public unsafe void PutStringUTF8(int offset, string value)
425 {
426 AssertOffsetAndLength(offset, value.Length);
427 fixed (char* s = value)
428 {
429 fixed (byte* buffer = &MemoryMarshal.GetReference(_buffer.Span))
430 {
431 Encoding.UTF8.GetBytes(s, value.Length, buffer + offset, Length - offset);
432 }
433 }
434 }
435#else
436 public void PutStringUTF8(int offset, string value)
437 {
438 AssertOffsetAndLength(offset, value.Length);
439 Encoding.UTF8.GetBytes(value, 0, value.Length,
440 _buffer.Buffer, offset);
441 }
442#endif
443
444#if UNSAFE_BYTEBUFFER
445 // Unsafe but more efficient versions of Put*.
446 public void PutShort(int offset, short value)
447 {
448 PutUshort(offset, (ushort)value);
449 }
450
451 public unsafe void PutUshort(int offset, ushort value)
452 {
453 AssertOffsetAndLength(offset, sizeof(ushort));
454#if ENABLE_SPAN_T
455 Span<byte> span = _buffer.Span.Slice(offset);
456 BinaryPrimitives.WriteUInt16LittleEndian(span, value);
457#else
458 fixed (byte* ptr = _buffer.Buffer)
459 {
460 *(ushort*)(ptr + offset) = BitConverter.IsLittleEndian
461 ? value
462 : ReverseBytes(value);
463 }
464#endif
465 }
466
467 public void PutInt(int offset, int value)
468 {
469 PutUint(offset, (uint)value);
470 }
471
472 public unsafe void PutUint(int offset, uint value)
473 {
474 AssertOffsetAndLength(offset, sizeof(uint));
475#if ENABLE_SPAN_T
476 Span<byte> span = _buffer.Span.Slice(offset);
477 BinaryPrimitives.WriteUInt32LittleEndian(span, value);
478#else
479 fixed (byte* ptr = _buffer.Buffer)
480 {
481 *(uint*)(ptr + offset) = BitConverter.IsLittleEndian
482 ? value
483 : ReverseBytes(value);
484 }
485#endif
486 }
487
488 public unsafe void PutLong(int offset, long value)
489 {
490 PutUlong(offset, (ulong)value);
491 }
492
493 public unsafe void PutUlong(int offset, ulong value)
494 {
495 AssertOffsetAndLength(offset, sizeof(ulong));
496#if ENABLE_SPAN_T
497 Span<byte> span = _buffer.Span.Slice(offset);
498 BinaryPrimitives.WriteUInt64LittleEndian(span, value);
499#else
500 fixed (byte* ptr = _buffer.Buffer)
501 {
502 *(ulong*)(ptr + offset) = BitConverter.IsLittleEndian
503 ? value
504 : ReverseBytes(value);
505 }
506#endif
507 }
508
509 public unsafe void PutFloat(int offset, float value)
510 {
511 AssertOffsetAndLength(offset, sizeof(float));
512#if ENABLE_SPAN_T
513 fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.Span))
514#else
515 fixed (byte* ptr = _buffer.Buffer)
516#endif
517 {
518 if (BitConverter.IsLittleEndian)
519 {
520 *(float*)(ptr + offset) = value;
521 }
522 else
523 {
524 *(uint*)(ptr + offset) = ReverseBytes(*(uint*)(&value));
525 }
526 }
527 }
528
529 public unsafe void PutDouble(int offset, double value)
530 {
531 AssertOffsetAndLength(offset, sizeof(double));
532#if ENABLE_SPAN_T
533 fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.Span))
534#else
535 fixed (byte* ptr = _buffer.Buffer)
536#endif
537 {
538 if (BitConverter.IsLittleEndian)
539 {
540 *(double*)(ptr + offset) = value;
541 }
542 else
543 {
544 *(ulong*)(ptr + offset) = ReverseBytes(*(ulong*)(&value));
545 }
546 }
547 }
548#else // !UNSAFE_BYTEBUFFER
549 // Slower versions of Put* for when unsafe code is not allowed.
550 public void PutShort(int offset, short value)
551 {
552 AssertOffsetAndLength(offset, sizeof(short));
553 WriteLittleEndian(offset, sizeof(short), (ulong)value);
554 }
555
556 public void PutUshort(int offset, ushort value)
557 {
558 AssertOffsetAndLength(offset, sizeof(ushort));
559 WriteLittleEndian(offset, sizeof(ushort), (ulong)value);
560 }
561
562 public void PutInt(int offset, int value)
563 {
564 AssertOffsetAndLength(offset, sizeof(int));
565 WriteLittleEndian(offset, sizeof(int), (ulong)value);
566 }
567
568 public void PutUint(int offset, uint value)
569 {
570 AssertOffsetAndLength(offset, sizeof(uint));
571 WriteLittleEndian(offset, sizeof(uint), (ulong)value);
572 }
573
574 public void PutLong(int offset, long value)
575 {
576 AssertOffsetAndLength(offset, sizeof(long));
577 WriteLittleEndian(offset, sizeof(long), (ulong)value);
578 }
579
580 public void PutUlong(int offset, ulong value)
581 {
582 AssertOffsetAndLength(offset, sizeof(ulong));
583 WriteLittleEndian(offset, sizeof(ulong), value);
584 }
585
586 public void PutFloat(int offset, float value)
587 {
588 AssertOffsetAndLength(offset, sizeof(float));
589 floathelper[0] = value;
590 Buffer.BlockCopy(floathelper, 0, inthelper, 0, sizeof(float));
591 WriteLittleEndian(offset, sizeof(float), (ulong)inthelper[0]);
592 }
593
594 public void PutDouble(int offset, double value)
595 {
596 AssertOffsetAndLength(offset, sizeof(double));
597 doublehelper[0] = value;
598 Buffer.BlockCopy(doublehelper, 0, ulonghelper, 0, sizeof(double));
599 WriteLittleEndian(offset, sizeof(double), ulonghelper[0]);
600 }
601
602#endif // UNSAFE_BYTEBUFFER
603
604#if ENABLE_SPAN_T
605 public sbyte GetSbyte(int index)
606 {
607 AssertOffsetAndLength(index, sizeof(sbyte));
608 return (sbyte)_buffer.ReadOnlySpan[index];
609 }
610
611 public byte Get(int index)
612 {
613 AssertOffsetAndLength(index, sizeof(byte));
614 return _buffer.ReadOnlySpan[index];
615 }
616#else
617 public sbyte GetSbyte(int index)
618 {
619 AssertOffsetAndLength(index, sizeof(sbyte));
620 return (sbyte)_buffer.Buffer[index];
621 }
622
623 public byte Get(int index)
624 {
625 AssertOffsetAndLength(index, sizeof(byte));
626 return _buffer.Buffer[index];
627 }
628#endif
629
630#if ENABLE_SPAN_T
631 public unsafe string GetStringUTF8(int startPos, int len)
632 {
633 fixed (byte* buffer = &MemoryMarshal.GetReference(_buffer.ReadOnlySpan.Slice(startPos)))
634 {
635 return Encoding.UTF8.GetString(buffer, len);
636 }
637 }
638#else
639 public string GetStringUTF8(int startPos, int len)
640 {
641 return Encoding.UTF8.GetString(_buffer.Buffer, startPos, len);
642 }
643#endif
644
645#if UNSAFE_BYTEBUFFER
646 // Unsafe but more efficient versions of Get*.
647 public short GetShort(int offset)
648 {
649 return (short)GetUshort(offset);
650 }
651
652 public unsafe ushort GetUshort(int offset)
653 {
654 AssertOffsetAndLength(offset, sizeof(ushort));
655#if ENABLE_SPAN_T
656 ReadOnlySpan<byte> span = _buffer.ReadOnlySpan.Slice(offset);
657 return BinaryPrimitives.ReadUInt16LittleEndian(span);
658#else
659 fixed (byte* ptr = _buffer.Buffer)
660 {
661 return BitConverter.IsLittleEndian
662 ? *(ushort*)(ptr + offset)
663 : ReverseBytes(*(ushort*)(ptr + offset));
664 }
665#endif
666 }
667
668 public int GetInt(int offset)
669 {
670 return (int)GetUint(offset);
671 }
672
673 public unsafe uint GetUint(int offset)
674 {
675 AssertOffsetAndLength(offset, sizeof(uint));
676#if ENABLE_SPAN_T
677 ReadOnlySpan<byte> span = _buffer.ReadOnlySpan.Slice(offset);
678 return BinaryPrimitives.ReadUInt32LittleEndian(span);
679#else
680 fixed (byte* ptr = _buffer.Buffer)
681 {
682 return BitConverter.IsLittleEndian
683 ? *(uint*)(ptr + offset)
684 : ReverseBytes(*(uint*)(ptr + offset));
685 }
686#endif
687 }
688
689 public long GetLong(int offset)
690 {
691 return (long)GetUlong(offset);
692 }
693
694 public unsafe ulong GetUlong(int offset)
695 {
696 AssertOffsetAndLength(offset, sizeof(ulong));
697#if ENABLE_SPAN_T
698 ReadOnlySpan<byte> span = _buffer.ReadOnlySpan.Slice(offset);
699 return BinaryPrimitives.ReadUInt64LittleEndian(span);
700#else
701 fixed (byte* ptr = _buffer.Buffer)
702 {
703 return BitConverter.IsLittleEndian
704 ? *(ulong*)(ptr + offset)
705 : ReverseBytes(*(ulong*)(ptr + offset));
706 }
707#endif
708 }
709
710 public unsafe float GetFloat(int offset)
711 {
712 AssertOffsetAndLength(offset, sizeof(float));
713#if ENABLE_SPAN_T
714 fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.ReadOnlySpan))
715#else
716 fixed (byte* ptr = _buffer.Buffer)
717#endif
718 {
719 if (BitConverter.IsLittleEndian)
720 {
721 return *(float*)(ptr + offset);
722 }
723 else
724 {
725 uint uvalue = ReverseBytes(*(uint*)(ptr + offset));
726 return *(float*)(&uvalue);
727 }
728 }
729 }
730
731 public unsafe double GetDouble(int offset)
732 {
733 AssertOffsetAndLength(offset, sizeof(double));
734#if ENABLE_SPAN_T
735 fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.ReadOnlySpan))
736#else
737 fixed (byte* ptr = _buffer.Buffer)
738#endif
739 {
740 if (BitConverter.IsLittleEndian)
741 {
742 return *(double*)(ptr + offset);
743 }
744 else
745 {
746 ulong uvalue = ReverseBytes(*(ulong*)(ptr + offset));
747 return *(double*)(&uvalue);
748 }
749 }
750 }
751#else // !UNSAFE_BYTEBUFFER
752 // Slower versions of Get* for when unsafe code is not allowed.
753 public short GetShort(int index)
754 {
755 return (short)ReadLittleEndian(index, sizeof(short));
756 }
757
758 public ushort GetUshort(int index)
759 {
760 return (ushort)ReadLittleEndian(index, sizeof(ushort));
761 }
762
763 public int GetInt(int index)
764 {
765 return (int)ReadLittleEndian(index, sizeof(int));
766 }
767
768 public uint GetUint(int index)
769 {
770 return (uint)ReadLittleEndian(index, sizeof(uint));
771 }
772
773 public long GetLong(int index)
774 {
775 return (long)ReadLittleEndian(index, sizeof(long));
776 }
777
778 public ulong GetUlong(int index)
779 {
780 return ReadLittleEndian(index, sizeof(ulong));
781 }
782
783 public float GetFloat(int index)
784 {
785 int i = (int)ReadLittleEndian(index, sizeof(float));
786 inthelper[0] = i;
787 Buffer.BlockCopy(inthelper, 0, floathelper, 0, sizeof(float));
788 return floathelper[0];
789 }
790
791 public double GetDouble(int index)
792 {
793 ulong i = ReadLittleEndian(index, sizeof(double));
794 // There's Int64BitsToDouble but it uses unsafe code internally.
795 ulonghelper[0] = i;
796 Buffer.BlockCopy(ulonghelper, 0, doublehelper, 0, sizeof(double));
797 return doublehelper[0];
798 }
799#endif // UNSAFE_BYTEBUFFER
800
801 /// <summary>
802 /// Copies an array of type T into this buffer, ending at the given
803 /// offset into this buffer. The starting offset is calculated based on the length
804 /// of the array and is the value returned.
805 /// </summary>
806 /// <typeparam name="T">The type of the input data (must be a struct)</typeparam>
807 /// <param name="offset">The offset into this buffer where the copy will end</param>
808 /// <param name="x">The array to copy data from</param>
809 /// <returns>The 'start' location of this buffer now, after the copy completed</returns>
810 public int Put<T>(int offset, T[] x)
811 where T : struct
812 {
813 if (x == null)
814 {
815 throw new ArgumentNullException("Cannot put a null array");
816 }
817
818 if (x.Length == 0)
819 {
820 throw new ArgumentException("Cannot put an empty array");
821 }
822
823 if (!IsSupportedType<T>())
824 {
825 throw new ArgumentException("Cannot put an array of type "
826 + typeof(T) + " into this buffer");
827 }
828
829 if (BitConverter.IsLittleEndian)
830 {
831 int numBytes = ByteBuffer.ArraySize(x);
832 offset -= numBytes;
833 AssertOffsetAndLength(offset, numBytes);
834 // if we are LE, just do a block copy
835#if ENABLE_SPAN_T
836 MemoryMarshal.Cast<T, byte>(x).CopyTo(_buffer.Span.Slice(offset, numBytes));
837#else
838 Buffer.BlockCopy(x, 0, _buffer.Buffer, offset, numBytes);
839#endif
840 }
841 else
842 {
843 throw new NotImplementedException("Big Endian Support not implemented yet " +
844 "for putting typed arrays");
845 // if we are BE, we have to swap each element by itself
846 //for(int i = x.Length - 1; i >= 0; i--)
847 //{
848 // todo: low priority, but need to genericize the Put<T>() functions
849 //}
850 }
851 return offset;
852 }
853
854#if ENABLE_SPAN_T
855 public int Put<T>(int offset, Span<T> x)
856 where T : struct
857 {
858 if (x.Length == 0)
859 {
860 throw new ArgumentException("Cannot put an empty array");
861 }
862
863 if (!IsSupportedType<T>())
864 {
865 throw new ArgumentException("Cannot put an array of type "
866 + typeof(T) + " into this buffer");
867 }
868
869 if (BitConverter.IsLittleEndian)
870 {
871 int numBytes = ByteBuffer.ArraySize(x);
872 offset -= numBytes;
873 AssertOffsetAndLength(offset, numBytes);
874 // if we are LE, just do a block copy
875 MemoryMarshal.Cast<T, byte>(x).CopyTo(_buffer.Span.Slice(offset, numBytes));
876 }
877 else
878 {
879 throw new NotImplementedException("Big Endian Support not implemented yet " +
880 "for putting typed arrays");
881 // if we are BE, we have to swap each element by itself
882 //for(int i = x.Length - 1; i >= 0; i--)
883 //{
884 // todo: low priority, but need to genericize the Put<T>() functions
885 //}
886 }
887 return offset;
888 }
889#endif
890 }
891}