1 // Copyright 2001-2019 Crytek GmbH / Crytek Group. All rights reserved.
4 using System
.Runtime
.InteropServices
;
6 namespace MiscUtil
.Conversion
9 /// Equivalent of System.BitConverter, but with either endianness.
11 public abstract class EndianBitConverter
13 #region Endianness of this converter
15 /// Indicates the byte order ("endianess") in which data is converted using this class.
18 /// Different computer architectures store data using different byte orders. "Big-endian"
19 /// means the most significant byte is on the left end of a word. "Little-endian" means the
20 /// most significant byte is on the right end of a word.
22 /// <returns>true if this converter is little-endian, false otherwise.</returns>
23 public abstract bool IsLittleEndian();
26 /// Indicates the byte order ("endianess") in which data is converted using this class.
28 public abstract Endianness Endianness { get; }
31 #region Factory properties
32 static LittleEndianBitConverter little
= new LittleEndianBitConverter();
34 /// Returns a little-endian bit converter instance. The same instance is
37 public static LittleEndianBitConverter Little
39 get { return little; }
42 static BigEndianBitConverter big
= new BigEndianBitConverter();
44 /// Returns a big-endian bit converter instance. The same instance is
47 public static BigEndianBitConverter Big
53 #region Double/primitive conversions
55 /// Converts the specified double-precision floating point number to a
56 /// 64-bit signed integer. Note: the endianness of this converter does not
57 /// affect the returned value.
59 /// <param name="value">The number to convert. </param>
60 /// <returns>A 64-bit signed integer whose value is equivalent to value.</returns>
61 public long DoubleToInt64Bits(double value)
63 return BitConverter
.DoubleToInt64Bits(value);
67 /// Converts the specified 64-bit signed integer to a double-precision
68 /// floating point number. Note: the endianness of this converter does not
69 /// affect the returned value.
71 /// <param name="value">The number to convert. </param>
72 /// <returns>A double-precision floating point number whose value is equivalent to value.</returns>
73 public double Int64BitsToDouble (long value)
75 return BitConverter
.Int64BitsToDouble(value);
79 /// Converts the specified single-precision floating point number to a
80 /// 32-bit signed integer. Note: the endianness of this converter does not
81 /// affect the returned value.
83 /// <param name="value">The number to convert. </param>
84 /// <returns>A 32-bit signed integer whose value is equivalent to value.</returns>
85 public int SingleToInt32Bits(float value)
87 return new Int32SingleUnion(value).AsInt32
;
91 /// Converts the specified 32-bit signed integer to a single-precision floating point
92 /// number. Note: the endianness of this converter does not
93 /// affect the returned value.
95 /// <param name="value">The number to convert. </param>
96 /// <returns>A single-precision floating point number whose value is equivalent to value.</returns>
97 public float Int32BitsToSingle (int value)
99 return new Int32SingleUnion(value).AsSingle
;
103 #region To(PrimitiveType) conversions
105 /// Returns a Boolean value converted from one byte at a specified position in a byte array.
107 /// <param name="value">An array of bytes.</param>
108 /// <param name="startIndex">The starting position within value.</param>
109 /// <returns>true if the byte at startIndex in value is nonzero; otherwise, false.</returns>
110 public bool ToBoolean (byte[] value, int startIndex
)
112 CheckByteArgument(value, startIndex
, 1);
113 return BitConverter
.ToBoolean(value, startIndex
);
117 /// Returns a Unicode character converted from two bytes at a specified position in a byte array.
119 /// <param name="value">An array of bytes.</param>
120 /// <param name="startIndex">The starting position within value.</param>
121 /// <returns>A character formed by two bytes beginning at startIndex.</returns>
122 public char ToChar (byte[] value, int startIndex
)
124 return unchecked((char) (CheckedFromBytes(value, startIndex
, 2)));
128 /// Returns a double-precision floating point number converted from eight bytes
129 /// at a specified position in a byte array.
131 /// <param name="value">An array of bytes.</param>
132 /// <param name="startIndex">The starting position within value.</param>
133 /// <returns>A double precision floating point number formed by eight bytes beginning at startIndex.</returns>
134 public double ToDouble (byte[] value, int startIndex
)
136 return Int64BitsToDouble(ToInt64(value, startIndex
));
140 /// Returns a single-precision floating point number converted from four bytes
141 /// at a specified position in a byte array.
143 /// <param name="value">An array of bytes.</param>
144 /// <param name="startIndex">The starting position within value.</param>
145 /// <returns>A single precision floating point number formed by four bytes beginning at startIndex.</returns>
146 public float ToSingle (byte[] value, int startIndex
)
148 return Int32BitsToSingle(ToInt32(value, startIndex
));
152 /// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array.
154 /// <param name="value">An array of bytes.</param>
155 /// <param name="startIndex">The starting position within value.</param>
156 /// <returns>A 16-bit signed integer formed by two bytes beginning at startIndex.</returns>
157 public short ToInt16 (byte[] value, int startIndex
)
159 return unchecked((short) (CheckedFromBytes(value, startIndex
, 2)));
163 /// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array.
165 /// <param name="value">An array of bytes.</param>
166 /// <param name="startIndex">The starting position within value.</param>
167 /// <returns>A 32-bit signed integer formed by four bytes beginning at startIndex.</returns>
168 public int ToInt32 (byte[] value, int startIndex
)
170 return unchecked((int) (CheckedFromBytes(value, startIndex
, 4)));
174 /// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array.
176 /// <param name="value">An array of bytes.</param>
177 /// <param name="startIndex">The starting position within value.</param>
178 /// <returns>A 64-bit signed integer formed by eight bytes beginning at startIndex.</returns>
179 public long ToInt64 (byte[] value, int startIndex
)
181 return CheckedFromBytes(value, startIndex
, 8);
185 /// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array.
187 /// <param name="value">An array of bytes.</param>
188 /// <param name="startIndex">The starting position within value.</param>
189 /// <returns>A 16-bit unsigned integer formed by two bytes beginning at startIndex.</returns>
190 public ushort ToUInt16 (byte[] value, int startIndex
)
192 return unchecked((ushort) (CheckedFromBytes(value, startIndex
, 2)));
196 /// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array.
198 /// <param name="value">An array of bytes.</param>
199 /// <param name="startIndex">The starting position within value.</param>
200 /// <returns>A 32-bit unsigned integer formed by four bytes beginning at startIndex.</returns>
201 public uint ToUInt32 (byte[] value, int startIndex
)
203 return unchecked((uint) (CheckedFromBytes(value, startIndex
, 4)));
207 /// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array.
209 /// <param name="value">An array of bytes.</param>
210 /// <param name="startIndex">The starting position within value.</param>
211 /// <returns>A 64-bit unsigned integer formed by eight bytes beginning at startIndex.</returns>
212 public ulong ToUInt64 (byte[] value, int startIndex
)
214 return unchecked((ulong) (CheckedFromBytes(value, startIndex
, 8)));
218 /// Checks the given argument for validity.
220 /// <param name="value">The byte array passed in</param>
221 /// <param name="startIndex">The start index passed in</param>
222 /// <param name="bytesRequired">The number of bytes required</param>
223 /// <exception cref="ArgumentNullException">value is a null reference</exception>
224 /// <exception cref="ArgumentOutOfRangeException">
225 /// startIndex is less than zero or greater than the length of value minus bytesRequired.
227 static void CheckByteArgument(byte[] value, int startIndex
, int bytesRequired
)
231 throw new ArgumentNullException("value");
233 if (startIndex
< 0 || startIndex
> value.Length
-bytesRequired
)
235 throw new ArgumentOutOfRangeException("startIndex");
240 /// Checks the arguments for validity before calling FromBytes
241 /// (which can therefore assume the arguments are valid).
243 /// <param name="value">The bytes to convert after checking</param>
244 /// <param name="startIndex">The index of the first byte to convert</param>
245 /// <param name="bytesToConvert">The number of bytes to convert</param>
246 /// <returns></returns>
247 long CheckedFromBytes(byte[] value, int startIndex
, int bytesToConvert
)
249 CheckByteArgument(value, startIndex
, bytesToConvert
);
250 return FromBytes(value, startIndex
, bytesToConvert
);
254 /// Convert the given number of bytes from the given array, from the given start
255 /// position, into a long, using the bytes as the least significant part of the long.
256 /// By the time this is called, the arguments have been checked for validity.
258 /// <param name="value">The bytes to convert</param>
259 /// <param name="startIndex">The index of the first byte to convert</param>
260 /// <param name="bytesToConvert">The number of bytes to use in the conversion</param>
261 /// <returns>The converted number</returns>
262 protected abstract long FromBytes(byte[] value, int startIndex
, int bytesToConvert
);
265 #region ToString conversions
267 /// Returns a String converted from the elements of a byte array.
269 /// <param name="value">An array of bytes.</param>
270 /// <remarks>All the elements of value are converted.</remarks>
272 /// A String of hexadecimal pairs separated by hyphens, where each pair
273 /// represents the corresponding element in value; for example, "7F-2C-4A".
275 public static string ToString(byte[] value)
277 return BitConverter
.ToString(value);
281 /// Returns a String converted from the elements of a byte array starting at a specified array position.
283 /// <param name="value">An array of bytes.</param>
284 /// <param name="startIndex">The starting position within value.</param>
285 /// <remarks>The elements from array position startIndex to the end of the array are converted.</remarks>
287 /// A String of hexadecimal pairs separated by hyphens, where each pair
288 /// represents the corresponding element in value; for example, "7F-2C-4A".
290 public static string ToString(byte[] value, int startIndex
)
292 return BitConverter
.ToString(value, startIndex
);
296 /// Returns a String converted from a specified number of bytes at a specified position in a byte array.
298 /// <param name="value">An array of bytes.</param>
299 /// <param name="startIndex">The starting position within value.</param>
300 /// <param name="length">The number of bytes to convert.</param>
301 /// <remarks>The length elements from array position startIndex are converted.</remarks>
303 /// A String of hexadecimal pairs separated by hyphens, where each pair
304 /// represents the corresponding element in value; for example, "7F-2C-4A".
306 public static string ToString(byte[] value, int startIndex
, int length
)
308 return BitConverter
.ToString(value, startIndex
, length
);
312 #region Decimal conversions
314 /// Returns a decimal value converted from sixteen bytes
315 /// at a specified position in a byte array.
317 /// <param name="value">An array of bytes.</param>
318 /// <param name="startIndex">The starting position within value.</param>
319 /// <returns>A decimal formed by sixteen bytes beginning at startIndex.</returns>
320 public decimal ToDecimal (byte[] value, int startIndex
)
322 // HACK: This always assumes four parts, each in their own endianness,
323 // starting with the first part at the start of the byte array.
324 // On the other hand, there's no real format specified...
325 int[] parts
= new int[4];
326 for (int i
=0; i
< 4; i
++)
328 parts
[i
] = ToInt32(value, startIndex
+i
*4);
330 return new Decimal(parts
);
334 /// Returns the specified decimal value as an array of bytes.
336 /// <param name="value">The number to convert.</param>
337 /// <returns>An array of bytes with length 16.</returns>
338 public byte[] GetBytes(decimal value)
340 byte[] bytes
= new byte[16];
341 int[] parts
= decimal.GetBits(value);
342 for (int i
=0; i
< 4; i
++)
344 CopyBytesImpl(parts
[i
], 4, bytes
, i
*4);
350 /// Copies the specified decimal value into the specified byte array,
351 /// beginning at the specified index.
353 /// <param name="value">A character to convert.</param>
354 /// <param name="buffer">The byte array to copy the bytes into</param>
355 /// <param name="index">The first index into the array to copy the bytes into</param>
356 public void CopyBytes(decimal value, byte[] buffer
, int index
)
358 int[] parts
= decimal.GetBits(value);
359 for (int i
=0; i
< 4; i
++)
361 CopyBytesImpl(parts
[i
], 4, buffer
, i
*4+index
);
366 #region GetBytes conversions
368 /// Returns an array with the given number of bytes formed
369 /// from the least significant bytes of the specified value.
370 /// This is used to implement the other GetBytes methods.
372 /// <param name="value">The value to get bytes for</param>
373 /// <param name="bytes">The number of significant bytes to return</param>
374 byte[] GetBytes(long value, int bytes
)
376 byte[] buffer
= new byte[bytes
];
377 CopyBytes(value, bytes
, buffer
, 0);
382 /// Returns the specified Boolean value as an array of bytes.
384 /// <param name="value">A Boolean value.</param>
385 /// <returns>An array of bytes with length 1.</returns>
386 public byte[] GetBytes(bool value)
388 return BitConverter
.GetBytes(value);
392 /// Returns the specified Unicode character value as an array of bytes.
394 /// <param name="value">A character to convert.</param>
395 /// <returns>An array of bytes with length 2.</returns>
396 public byte[] GetBytes(char value)
398 return GetBytes(value, 2);
402 /// Returns the specified double-precision floating point value as an array of bytes.
404 /// <param name="value">The number to convert.</param>
405 /// <returns>An array of bytes with length 8.</returns>
406 public byte[] GetBytes(double value)
408 return GetBytes(DoubleToInt64Bits(value), 8);
412 /// Returns the specified 16-bit signed integer value as an array of bytes.
414 /// <param name="value">The number to convert.</param>
415 /// <returns>An array of bytes with length 2.</returns>
416 public byte[] GetBytes(short value)
418 return GetBytes(value, 2);
422 /// Returns the specified 32-bit signed integer value as an array of bytes.
424 /// <param name="value">The number to convert.</param>
425 /// <returns>An array of bytes with length 4.</returns>
426 public byte[] GetBytes(int value)
428 return GetBytes(value, 4);
432 /// Returns the specified 64-bit signed integer value as an array of bytes.
434 /// <param name="value">The number to convert.</param>
435 /// <returns>An array of bytes with length 8.</returns>
436 public byte[] GetBytes(long value)
438 return GetBytes(value, 8);
442 /// Returns the specified single-precision floating point value as an array of bytes.
444 /// <param name="value">The number to convert.</param>
445 /// <returns>An array of bytes with length 4.</returns>
446 public byte[] GetBytes(float value)
448 return GetBytes(SingleToInt32Bits(value), 4);
452 /// Returns the specified 16-bit unsigned integer value as an array of bytes.
454 /// <param name="value">The number to convert.</param>
455 /// <returns>An array of bytes with length 2.</returns>
456 public byte[] GetBytes(ushort value)
458 return GetBytes(value, 2);
462 /// Returns the specified 32-bit unsigned integer value as an array of bytes.
464 /// <param name="value">The number to convert.</param>
465 /// <returns>An array of bytes with length 4.</returns>
466 public byte[] GetBytes(uint value)
468 return GetBytes(value, 4);
472 /// Returns the specified 64-bit unsigned integer value as an array of bytes.
474 /// <param name="value">The number to convert.</param>
475 /// <returns>An array of bytes with length 8.</returns>
476 public byte[] GetBytes(ulong value)
478 return GetBytes(unchecked((long)value), 8);
483 #region CopyBytes conversions
485 /// Copies the given number of bytes from the least-specific
486 /// end of the specified value into the specified byte array, beginning
487 /// at the specified index.
488 /// This is used to implement the other CopyBytes methods.
490 /// <param name="value">The value to copy bytes for</param>
491 /// <param name="bytes">The number of significant bytes to copy</param>
492 /// <param name="buffer">The byte array to copy the bytes into</param>
493 /// <param name="index">The first index into the array to copy the bytes into</param>
494 void CopyBytes(long value, int bytes
, byte[] buffer
, int index
)
498 throw new ArgumentNullException("buffer", "Byte array must not be null");
500 if (buffer
.Length
< index
+bytes
)
502 throw new ArgumentOutOfRangeException("Buffer not big enough for value");
504 CopyBytesImpl(value, bytes
, buffer
, index
);
508 /// Copies the given number of bytes from the least-specific
509 /// end of the specified value into the specified byte array, beginning
510 /// at the specified index.
511 /// This must be implemented in concrete derived classes, but the implementation
512 /// may assume that the value will fit into the buffer.
514 /// <param name="value">The value to copy bytes for</param>
515 /// <param name="bytes">The number of significant bytes to copy</param>
516 /// <param name="buffer">The byte array to copy the bytes into</param>
517 /// <param name="index">The first index into the array to copy the bytes into</param>
518 protected abstract void CopyBytesImpl(long value, int bytes
, byte[] buffer
, int index
);
521 /// Copies the specified Boolean value into the specified byte array,
522 /// beginning at the specified index.
524 /// <param name="value">A Boolean value.</param>
525 /// <param name="buffer">The byte array to copy the bytes into</param>
526 /// <param name="index">The first index into the array to copy the bytes into</param>
527 public void CopyBytes(bool value, byte[] buffer
, int index
)
529 CopyBytes(value ? 1 : 0, 1, buffer
, index
);
533 /// Copies the specified Unicode character value into the specified byte array,
534 /// beginning at the specified index.
536 /// <param name="value">A character to convert.</param>
537 /// <param name="buffer">The byte array to copy the bytes into</param>
538 /// <param name="index">The first index into the array to copy the bytes into</param>
539 public void CopyBytes(char value, byte[] buffer
, int index
)
541 CopyBytes(value, 2, buffer
, index
);
545 /// Copies the specified double-precision floating point value into the specified byte array,
546 /// beginning at the specified index.
548 /// <param name="value">The number to convert.</param>
549 /// <param name="buffer">The byte array to copy the bytes into</param>
550 /// <param name="index">The first index into the array to copy the bytes into</param>
551 public void CopyBytes(double value, byte[] buffer
, int index
)
553 CopyBytes(DoubleToInt64Bits(value), 8, buffer
, index
);
557 /// Copies the specified 16-bit signed integer value into the specified byte array,
558 /// beginning at the specified index.
560 /// <param name="value">The number to convert.</param>
561 /// <param name="buffer">The byte array to copy the bytes into</param>
562 /// <param name="index">The first index into the array to copy the bytes into</param>
563 public void CopyBytes(short value, byte[] buffer
, int index
)
565 CopyBytes(value, 2, buffer
, index
);
569 /// Copies the specified 32-bit signed integer value into the specified byte array,
570 /// beginning at the specified index.
572 /// <param name="value">The number to convert.</param>
573 /// <param name="buffer">The byte array to copy the bytes into</param>
574 /// <param name="index">The first index into the array to copy the bytes into</param>
575 public void CopyBytes(int value, byte[] buffer
, int index
)
577 CopyBytes(value, 4, buffer
, index
);
581 /// Copies the specified 64-bit signed integer value into the specified byte array,
582 /// beginning at the specified index.
584 /// <param name="value">The number to convert.</param>
585 /// <param name="buffer">The byte array to copy the bytes into</param>
586 /// <param name="index">The first index into the array to copy the bytes into</param>
587 public void CopyBytes(long value, byte[] buffer
, int index
)
589 CopyBytes(value, 8, buffer
, index
);
593 /// Copies the specified single-precision floating point value into the specified byte array,
594 /// beginning at the specified index.
596 /// <param name="value">The number to convert.</param>
597 /// <param name="buffer">The byte array to copy the bytes into</param>
598 /// <param name="index">The first index into the array to copy the bytes into</param>
599 public void CopyBytes(float value, byte[] buffer
, int index
)
601 CopyBytes(SingleToInt32Bits(value), 4, buffer
, index
);
605 /// Copies the specified 16-bit unsigned integer value into the specified byte array,
606 /// beginning at the specified index.
608 /// <param name="value">The number to convert.</param>
609 /// <param name="buffer">The byte array to copy the bytes into</param>
610 /// <param name="index">The first index into the array to copy the bytes into</param>
611 public void CopyBytes(ushort value, byte[] buffer
, int index
)
613 CopyBytes(value, 2, buffer
, index
);
617 /// Copies the specified 32-bit unsigned integer value into the specified byte array,
618 /// beginning at the specified index.
620 /// <param name="value">The number to convert.</param>
621 /// <param name="buffer">The byte array to copy the bytes into</param>
622 /// <param name="index">The first index into the array to copy the bytes into</param>
623 public void CopyBytes(uint value, byte[] buffer
, int index
)
625 CopyBytes(value, 4, buffer
, index
);
629 /// Copies the specified 64-bit unsigned integer value into the specified byte array,
630 /// beginning at the specified index.
632 /// <param name="value">The number to convert.</param>
633 /// <param name="buffer">The byte array to copy the bytes into</param>
634 /// <param name="index">The first index into the array to copy the bytes into</param>
635 public void CopyBytes(ulong value, byte[] buffer
, int index
)
637 CopyBytes(unchecked((long)value), 8, buffer
, index
);
642 #region Private struct used for Single/Int32 conversions
644 /// Union used solely for the equivalent of DoubleToInt64Bits and vice versa.
646 [StructLayout(LayoutKind
.Explicit
)]
647 struct Int32SingleUnion
650 /// Int32 version of the value.
655 /// Single version of the value.
661 /// Creates an instance representing the given integer.
663 /// <param name="i">The integer value of the new instance.</param>
664 internal Int32SingleUnion(int i
)
666 this.f
= 0; // Just to keep the compiler happy
671 /// Creates an instance representing the given floating point number.
673 /// <param name="f">The floating point value of the new instance.</param>
674 internal Int32SingleUnion(float f
)
676 this.i
= 0; // Just to keep the compiler happy
681 /// Returns the value of the instance as an integer.
689 /// Returns the value of the instance as a floating point number.
691 internal float AsSingle