Added tests for Task.WhenAll w/ empty list
[mono.git] / mcs / class / corlib / System / BitConverter.cs
1 //
2 // System.BitConverter.cs
3 //
4 // Author:
5 //   Matt Kimball (matt@kimball.net)
6 //
7 //
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System.Text;
31
32 namespace System
33 {
34         public
35         static
36         class BitConverter
37         {
38                 static readonly bool SwappedWordsInDouble = DoubleWordsAreSwapped ();
39                 public static readonly bool IsLittleEndian = AmILittleEndian ();
40
41                 static unsafe bool AmILittleEndian ()
42                 {
43                         // binary representations of 1.0:
44                         // big endian: 3f f0 00 00 00 00 00 00
45                         // little endian: 00 00 00 00 00 00 f0 3f
46                         // arm fpa little endian: 00 00 f0 3f 00 00 00 00
47                         double d = 1.0;
48                         byte *b = (byte*)&d;
49                         return (b [0] == 0);
50                 }
51
52                 static unsafe bool DoubleWordsAreSwapped ()
53                 {
54                         // binary representations of 1.0:
55                         // big endian: 3f f0 00 00 00 00 00 00
56                         // little endian: 00 00 00 00 00 00 f0 3f
57                         // arm fpa little endian: 00 00 f0 3f 00 00 00 00
58                         double d = 1.0;
59                         byte *b = (byte*)&d;
60                         return b [2] == 0xf0;
61                 }
62
63                 public unsafe static long DoubleToInt64Bits (double value)
64                 {
65                         return *(long *) &value;
66                 }
67
68                 public unsafe static double Int64BitsToDouble (long value)
69                 {
70                         return *(double *) &value;
71                 }
72
73                 internal static double InternalInt64BitsToDouble (long value)
74                 {
75                         return SwappableToDouble (GetBytes (value), 0);
76                 }
77                 
78                 unsafe static byte[] GetBytes (byte *ptr, int count)
79                 {
80                         byte [] ret = new byte [count];
81
82                         for (int i = 0; i < count; i++) {
83                                 ret [i] = ptr [i];
84                         }
85
86                         return ret;
87                 }
88
89                 unsafe public static byte[] GetBytes (bool value)
90                 {
91                         return GetBytes ((byte *) &value, 1);
92                 }
93
94                 unsafe public static byte[] GetBytes (char value)
95                 {
96                         return GetBytes ((byte *) &value, 2);
97                 }
98
99                 unsafe public static byte[] GetBytes (short value)
100                 {
101                         return GetBytes ((byte *) &value, 2);
102                 }
103
104                 unsafe public static byte[] GetBytes (int value)
105                 {
106                         return GetBytes ((byte *) &value, 4);
107                 }
108
109                 unsafe public static byte[] GetBytes (long value)
110                 {
111                         return GetBytes ((byte *) &value, 8);
112                 }
113
114                 [CLSCompliant (false)]
115                 unsafe public static byte[] GetBytes (ushort value)
116                 {
117                         return GetBytes ((byte *) &value, 2);
118                 }
119
120                 [CLSCompliant (false)]
121                 unsafe public static byte[] GetBytes (uint value)
122                 {
123                         return GetBytes ((byte *) &value, 4);
124                 }
125
126                 [CLSCompliant (false)]
127                 unsafe public static byte[] GetBytes (ulong value)
128                 {
129                         return GetBytes ((byte *) &value, 8);
130                 }
131
132                 unsafe public static byte[] GetBytes (float value)
133                 {
134                         return GetBytes ((byte *) &value, 4);
135                 }
136
137                 unsafe public static byte[] GetBytes (double value)
138                 {
139                         if (SwappedWordsInDouble) {
140                                 byte[] data = new byte [8];
141                                 byte *p = (byte*)&value;
142                                 data [0] = p [4];
143                                 data [1] = p [5];
144                                 data [2] = p [6];
145                                 data [3] = p [7];
146                                 data [4] = p [0];
147                                 data [5] = p [1];
148                                 data [6] = p [2];
149                                 data [7] = p [3];
150                                 return data;
151                         } else {
152                                 return GetBytes ((byte *) &value, 8);
153                         }
154                 }
155
156                 unsafe static void PutBytes (byte *dst, byte[] src, int start_index, int count)
157                 {
158                         if (src == null)
159                                 throw new ArgumentNullException ("value");
160
161                         if (start_index < 0 || (start_index > src.Length - 1))
162                                 throw new ArgumentOutOfRangeException ("startIndex", "Index was"
163                                         + " out of range. Must be non-negative and less than the"
164                                         + " size of the collection.");
165
166                         // avoid integer overflow (with large pos/neg start_index values)
167                         if (src.Length - count < start_index)
168                                 throw new ArgumentException ("Destination array is not long"
169                                         + " enough to copy all the items in the collection."
170                                         + " Check array index and length.");
171
172                         for (int i = 0; i < count; i++)
173                                 dst[i] = src[i + start_index];
174                 }
175
176                 unsafe public static bool ToBoolean (byte[] value, int startIndex)
177                 {
178                         if (value == null) 
179                                 throw new ArgumentNullException ("value");
180
181                         if (startIndex < 0 || (startIndex > value.Length - 1))
182                                 throw new ArgumentOutOfRangeException ("startIndex", "Index was"
183                                         + " out of range. Must be non-negative and less than the"
184                                         + " size of the collection.");
185
186                         if (value [startIndex] != 0)
187                                 return true;
188                         
189                         return false;
190                 }
191
192                 unsafe public static char ToChar (byte[] value, int startIndex)
193                 {
194                         char ret;
195
196                         PutBytes ((byte *) &ret, value, startIndex, 2);
197
198                         return ret;
199                 }
200
201                 unsafe public static short ToInt16 (byte[] value, int startIndex)
202                 {
203                         short ret;
204
205                         PutBytes ((byte *) &ret, value, startIndex, 2);
206
207                         return ret;
208                 }
209
210                 unsafe public static int ToInt32 (byte[] value, int startIndex)
211                 {
212                         int ret;
213
214                         PutBytes ((byte *) &ret, value, startIndex, 4);
215
216                         return ret;
217                 }
218
219                 unsafe public static long ToInt64 (byte[] value, int startIndex)
220                 {
221                         long ret;
222
223                         PutBytes ((byte *) &ret, value, startIndex, 8);
224
225                         return ret;
226                 }
227
228                 [CLSCompliant (false)]
229                 unsafe public static ushort ToUInt16 (byte[] value, int startIndex)
230                 {
231                         ushort ret;
232
233                         PutBytes ((byte *) &ret, value, startIndex, 2);
234
235                         return ret;
236                 }
237
238                 [CLSCompliant (false)]
239                 unsafe public static uint ToUInt32 (byte[] value, int startIndex)
240                 {
241                         uint ret;
242
243                         PutBytes ((byte *) &ret, value, startIndex, 4);
244
245                         return ret;
246                 }
247
248                 [CLSCompliant (false)]
249                 unsafe public static ulong ToUInt64 (byte[] value, int startIndex)
250                 {
251                         ulong ret;
252
253                         PutBytes ((byte *) &ret, value, startIndex, 8);
254
255                         return ret;
256                 }
257
258                 unsafe public static float ToSingle (byte[] value, int startIndex)
259                 {
260                         float ret;
261
262                         PutBytes ((byte *) &ret, value, startIndex, 4);
263
264                         return ret;
265                 }
266
267                 unsafe public static double ToDouble (byte[] value, int startIndex)
268                 {
269                         double ret;
270
271                         if (SwappedWordsInDouble) {
272                                 byte* p = (byte*)&ret;
273                                 if (value == null)
274                                         throw new ArgumentNullException ("value");
275
276                                 if (startIndex < 0 || (startIndex > value.Length - 1))
277                                         throw new ArgumentOutOfRangeException ("startIndex", "Index was"
278                                                 + " out of range. Must be non-negative and less than the"
279                                                 + " size of the collection.");
280
281                                 // avoid integer overflow (with large pos/neg start_index values)
282                                 if (value.Length - 8 < startIndex)
283                                         throw new ArgumentException ("Destination array is not long"
284                                                 + " enough to copy all the items in the collection."
285                                                 + " Check array index and length.");
286
287                                 p [0] = value [startIndex + 4];
288                                 p [1] = value [startIndex + 5];
289                                 p [2] = value [startIndex + 6];
290                                 p [3] = value [startIndex + 7];
291                                 p [4] = value [startIndex + 0];
292                                 p [5] = value [startIndex + 1];
293                                 p [6] = value [startIndex + 2];
294                                 p [7] = value [startIndex + 3];
295
296                                 return ret;
297                         }
298
299                         PutBytes ((byte *) &ret, value, startIndex, 8);
300
301                         return ret;
302                 }
303
304                 unsafe internal static double SwappableToDouble (byte[] value, int startIndex)
305                 {
306                         double ret;
307
308                         if (SwappedWordsInDouble) {
309                                 byte* p = (byte*)&ret;
310                                 if (value == null)
311                                         throw new ArgumentNullException ("value");
312
313                                 if (startIndex < 0 || (startIndex > value.Length - 1))
314                                         throw new ArgumentOutOfRangeException ("startIndex", "Index was"
315                                                 + " out of range. Must be non-negative and less than the"
316                                                 + " size of the collection.");
317
318                                 // avoid integer overflow (with large pos/neg start_index values)
319                                 if (value.Length - 8 < startIndex)
320                                         throw new ArgumentException ("Destination array is not long"
321                                                 + " enough to copy all the items in the collection."
322                                                 + " Check array index and length.");
323
324                                 p [0] = value [startIndex + 4];
325                                 p [1] = value [startIndex + 5];
326                                 p [2] = value [startIndex + 6];
327                                 p [3] = value [startIndex + 7];
328                                 p [4] = value [startIndex + 0];
329                                 p [5] = value [startIndex + 1];
330                                 p [6] = value [startIndex + 2];
331                                 p [7] = value [startIndex + 3];
332
333                                 return ret;
334                         } else if (!IsLittleEndian) {
335                                 byte* p = (byte*)&ret;
336                                 if (value == null)
337                                         throw new ArgumentNullException ("value");
338
339                                 if (startIndex < 0 || (startIndex > value.Length - 1))
340                                         throw new ArgumentOutOfRangeException ("startIndex", "Index was"
341                                                 + " out of range. Must be non-negative and less than the"
342                                                 + " size of the collection.");
343
344                                 // avoid integer overflow (with large pos/neg start_index values)
345                                 if (value.Length - 8 < startIndex)
346                                         throw new ArgumentException ("Destination array is not long"
347                                                 + " enough to copy all the items in the collection."
348                                                 + " Check array index and length.");
349
350                                 p [0] = value [startIndex + 7];
351                                 p [1] = value [startIndex + 6];
352                                 p [2] = value [startIndex + 5];
353                                 p [3] = value [startIndex + 4];
354                                 p [4] = value [startIndex + 3];
355                                 p [5] = value [startIndex + 2];
356                                 p [6] = value [startIndex + 1];
357                                 p [7] = value [startIndex + 0];
358
359                                 return ret;
360                         }
361
362                         PutBytes ((byte *) &ret, value, startIndex, 8);
363
364                         return ret;
365                 }
366                 
367                 public static string ToString (byte[] value)
368                 {
369                         if (value == null)
370                                 throw new ArgumentNullException ("value");
371
372                         return ToString (value, 0, value.Length);
373                 }
374
375                 public static string ToString (byte[] value, int startIndex)
376                 {
377                         if (value == null)
378                                 throw new ArgumentNullException ("value");
379
380                         return ToString (value, startIndex, value.Length - startIndex);
381                 }
382
383                 public static string ToString (byte[] value, int startIndex, int length)
384                 {
385                         if (value == null)
386                                 throw new ArgumentNullException ("byteArray");
387
388                         // The 4th and last clause (start_index >= value.Length)
389                         // was added as a small fix to a very obscure bug.
390                         // It makes a small difference when start_index is
391                         // outside the range and length==0. 
392                         if (startIndex < 0 || startIndex >= value.Length) {
393                                 // special (but valid) case (e.g. new byte [0])
394                                 if ((startIndex == 0) && (value.Length == 0))
395                                         return String.Empty;
396                                 throw new ArgumentOutOfRangeException ("startIndex", "Index was"
397                                         + " out of range. Must be non-negative and less than the"
398                                         + " size of the collection.");
399                         }
400
401                         if (length < 0)
402                                 throw new ArgumentOutOfRangeException ("length",
403                                         "Value must be positive.");
404
405                         // note: re-ordered to avoid possible integer overflow
406                         if (startIndex > value.Length - length)
407                                 throw new ArgumentException ("startIndex + length > value.Length");
408
409                         if (length == 0)
410                                 return string.Empty;
411
412                         StringBuilder builder = new StringBuilder(length * 3 - 1);
413                         int end = startIndex + length;
414
415                         for (int i = startIndex; i < end; i++) {
416                                 if (i > startIndex)
417                                         builder.Append('-');
418                                 
419                                 char high = (char)((value[i] >> 4) & 0x0f);
420                                 char low = (char)(value[i] & 0x0f);
421
422                                 if (high < 10) 
423                                         high += '0';
424                                 else {
425                                         high -= (char) 10;
426                                         high += 'A';
427                                 }
428
429                                 if (low < 10)
430                                         low += '0';
431                                 else {
432                                         low -= (char) 10;
433                                         low += 'A';
434                                 }
435                                 builder.Append(high);
436                                 builder.Append(low);
437                         }
438
439                         return builder.ToString ();
440                 }
441         }
442 }