2007-11-14 Zoltan Varga <vargaz@gmail.com>
[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 #if NET_2_0
36         static
37 #else
38         sealed
39 #endif
40         class BitConverter
41         {
42 #if !NET_2_0
43                 private BitConverter ()
44                 {
45                 }
46 #endif
47
48                 static readonly bool SwappedWordsInDouble = DoubleWordsAreSwapped ();
49                 public static readonly bool IsLittleEndian = AmILittleEndian ();
50
51                 static unsafe bool AmILittleEndian ()
52                 {
53                         // binary representations of 1.0:
54                         // big endian: 3f f0 00 00 00 00 00 00
55                         // little endian: 00 00 00 00 00 00 f0 3f
56                         // arm fpa little endian: 00 00 f0 3f 00 00 00 00
57                         double d = 1.0;
58                         byte *b = (byte*)&d;
59                         return (b [0] == 0);
60                 }
61
62                 static unsafe bool DoubleWordsAreSwapped ()
63                 {
64                         // binary representations of 1.0:
65                         // big endian: 3f f0 00 00 00 00 00 00
66                         // little endian: 00 00 00 00 00 00 f0 3f
67                         // arm fpa little endian: 00 00 f0 3f 00 00 00 00
68                         double d = 1.0;
69                         byte *b = (byte*)&d;
70                         return b [2] == 0xf0;
71                 }
72
73                 public static long DoubleToInt64Bits (double value)
74                 {
75                         return ToInt64 (GetBytes (value), 0);
76                 }
77
78                 public static double Int64BitsToDouble (long value)
79                 {
80                         return ToDouble (GetBytes (value), 0);
81                 }
82
83                 internal static double InternalInt64BitsToDouble (long value)
84                 {
85                         return SwappableToDouble (GetBytes (value), 0);
86                 }
87                 
88                 unsafe static byte[] GetBytes (byte *ptr, int count)
89                 {
90                         byte [] ret = new byte [count];
91
92                         for (int i = 0; i < count; i++) {
93                                 ret [i] = ptr [i];
94                         }
95
96                         return ret;
97                 }
98
99                 unsafe public static byte[] GetBytes (bool value)
100                 {
101                         return GetBytes ((byte *) &value, 1);
102                 }
103
104                 unsafe public static byte[] GetBytes (char value)
105                 {
106                         return GetBytes ((byte *) &value, 2);
107                 }
108
109                 unsafe public static byte[] GetBytes (short value)
110                 {
111                         return GetBytes ((byte *) &value, 2);
112                 }
113
114                 unsafe public static byte[] GetBytes (int value)
115                 {
116                         return GetBytes ((byte *) &value, 4);
117                 }
118
119                 unsafe public static byte[] GetBytes (long value)
120                 {
121                         return GetBytes ((byte *) &value, 8);
122                 }
123
124                 [CLSCompliant (false)]
125                 unsafe public static byte[] GetBytes (ushort value)
126                 {
127                         return GetBytes ((byte *) &value, 2);
128                 }
129
130                 [CLSCompliant (false)]
131                 unsafe public static byte[] GetBytes (uint value)
132                 {
133                         return GetBytes ((byte *) &value, 4);
134                 }
135
136                 [CLSCompliant (false)]
137                 unsafe public static byte[] GetBytes (ulong value)
138                 {
139                         return GetBytes ((byte *) &value, 8);
140                 }
141
142                 unsafe public static byte[] GetBytes (float value)
143                 {
144                         return GetBytes ((byte *) &value, 4);
145                 }
146
147                 unsafe public static byte[] GetBytes (double value)
148                 {
149                         if (SwappedWordsInDouble) {
150                                 byte[] data = new byte [8];
151                                 byte *p = (byte*)&value;
152                                 data [0] = p [4];
153                                 data [1] = p [5];
154                                 data [2] = p [6];
155                                 data [3] = p [7];
156                                 data [4] = p [0];
157                                 data [5] = p [1];
158                                 data [6] = p [2];
159                                 data [7] = p [3];
160                                 return data;
161                         } else {
162                                 return GetBytes ((byte *) &value, 8);
163                         }
164                 }
165
166                 unsafe static void PutBytes (byte *dst, byte[] src, int start_index, int count)
167                 {
168                         if (src == null)
169 #if NET_2_0
170                                 throw new ArgumentNullException ("value");
171 #else
172                                 throw new ArgumentNullException ("byteArray");
173 #endif
174
175                         if (start_index < 0 || (start_index > src.Length - 1))
176                                 throw new ArgumentOutOfRangeException ("startIndex", "Index was"
177                                         + " out of range. Must be non-negative and less than the"
178                                         + " size of the collection.");
179
180                         // avoid integer overflow (with large pos/neg start_index values)
181                         if (src.Length - count < start_index)
182                                 throw new ArgumentException ("Destination array is not long"
183                                         + " enough to copy all the items in the collection."
184                                         + " Check array index and length.");
185
186                         for (int i = 0; i < count; i++)
187                                 dst[i] = src[i + start_index];
188                 }
189
190                 unsafe public static bool ToBoolean (byte[] value, int startIndex)
191                 {
192                         if (value == null) 
193                                 throw new ArgumentNullException ("value");
194
195                         if (startIndex < 0 || (startIndex > value.Length - 1))
196                                 throw new ArgumentOutOfRangeException ("startIndex", "Index was"
197                                         + " out of range. Must be non-negative and less than the"
198                                         + " size of the collection.");
199
200                         if (value [startIndex] != 0)
201                                 return true;
202                         
203                         return false;
204                 }
205
206                 unsafe public static char ToChar (byte[] value, int startIndex)
207                 {
208                         char ret;
209
210                         PutBytes ((byte *) &ret, value, startIndex, 2);
211
212                         return ret;
213                 }
214
215                 unsafe public static short ToInt16 (byte[] value, int startIndex)
216                 {
217                         short ret;
218
219                         PutBytes ((byte *) &ret, value, startIndex, 2);
220
221                         return ret;
222                 }
223
224                 unsafe public static int ToInt32 (byte[] value, int startIndex)
225                 {
226                         int ret;
227
228                         PutBytes ((byte *) &ret, value, startIndex, 4);
229
230                         return ret;
231                 }
232
233                 unsafe public static long ToInt64 (byte[] value, int startIndex)
234                 {
235                         long ret;
236
237                         PutBytes ((byte *) &ret, value, startIndex, 8);
238
239                         return ret;
240                 }
241
242                 [CLSCompliant (false)]
243                 unsafe public static ushort ToUInt16 (byte[] value, int startIndex)
244                 {
245                         ushort ret;
246
247                         PutBytes ((byte *) &ret, value, startIndex, 2);
248
249                         return ret;
250                 }
251
252                 [CLSCompliant (false)]
253                 unsafe public static uint ToUInt32 (byte[] value, int startIndex)
254                 {
255                         uint ret;
256
257                         PutBytes ((byte *) &ret, value, startIndex, 4);
258
259                         return ret;
260                 }
261
262                 [CLSCompliant (false)]
263                 unsafe public static ulong ToUInt64 (byte[] value, int startIndex)
264                 {
265                         ulong ret;
266
267                         PutBytes ((byte *) &ret, value, startIndex, 8);
268
269                         return ret;
270                 }
271
272                 unsafe public static float ToSingle (byte[] value, int startIndex)
273                 {
274                         float ret;
275
276                         PutBytes ((byte *) &ret, value, startIndex, 4);
277
278                         return ret;
279                 }
280
281                 unsafe public static double ToDouble (byte[] value, int startIndex)
282                 {
283                         double ret;
284
285                         if (SwappedWordsInDouble) {
286                                 byte* p = (byte*)&ret;
287                                 if (value == null)
288                                         throw new ArgumentNullException ("value");
289
290                                 if (startIndex < 0 || (startIndex > value.Length - 1))
291                                         throw new ArgumentOutOfRangeException ("startIndex", "Index was"
292                                                 + " out of range. Must be non-negative and less than the"
293                                                 + " size of the collection.");
294
295                                 // avoid integer overflow (with large pos/neg start_index values)
296                                 if (value.Length - 8 < startIndex)
297                                         throw new ArgumentException ("Destination array is not long"
298                                                 + " enough to copy all the items in the collection."
299                                                 + " Check array index and length.");
300
301                                 p [0] = value [startIndex + 4];
302                                 p [1] = value [startIndex + 5];
303                                 p [2] = value [startIndex + 6];
304                                 p [3] = value [startIndex + 7];
305                                 p [4] = value [startIndex + 0];
306                                 p [5] = value [startIndex + 1];
307                                 p [6] = value [startIndex + 2];
308                                 p [7] = value [startIndex + 3];
309
310                                 return ret;
311                         }
312
313                         PutBytes ((byte *) &ret, value, startIndex, 8);
314
315                         return ret;
316                 }
317
318                 unsafe internal static double SwappableToDouble (byte[] value, int startIndex)
319                 {
320                         double ret;
321
322                         if (SwappedWordsInDouble) {
323                                 byte* p = (byte*)&ret;
324                                 if (value == null)
325                                         throw new ArgumentNullException ("value");
326
327                                 if (startIndex < 0 || (startIndex > value.Length - 1))
328                                         throw new ArgumentOutOfRangeException ("startIndex", "Index was"
329                                                 + " out of range. Must be non-negative and less than the"
330                                                 + " size of the collection.");
331
332                                 // avoid integer overflow (with large pos/neg start_index values)
333                                 if (value.Length - 8 < startIndex)
334                                         throw new ArgumentException ("Destination array is not long"
335                                                 + " enough to copy all the items in the collection."
336                                                 + " Check array index and length.");
337
338                                 p [0] = value [startIndex + 4];
339                                 p [1] = value [startIndex + 5];
340                                 p [2] = value [startIndex + 6];
341                                 p [3] = value [startIndex + 7];
342                                 p [4] = value [startIndex + 0];
343                                 p [5] = value [startIndex + 1];
344                                 p [6] = value [startIndex + 2];
345                                 p [7] = value [startIndex + 3];
346
347                                 return ret;
348                         } else if (!IsLittleEndian) {
349                                 byte* p = (byte*)&ret;
350                                 if (value == null)
351                                         throw new ArgumentNullException ("value");
352
353                                 if (startIndex < 0 || (startIndex > value.Length - 1))
354                                         throw new ArgumentOutOfRangeException ("startIndex", "Index was"
355                                                 + " out of range. Must be non-negative and less than the"
356                                                 + " size of the collection.");
357
358                                 // avoid integer overflow (with large pos/neg start_index values)
359                                 if (value.Length - 8 < startIndex)
360                                         throw new ArgumentException ("Destination array is not long"
361                                                 + " enough to copy all the items in the collection."
362                                                 + " Check array index and length.");
363
364                                 p [0] = value [startIndex + 7];
365                                 p [1] = value [startIndex + 6];
366                                 p [2] = value [startIndex + 5];
367                                 p [3] = value [startIndex + 4];
368                                 p [4] = value [startIndex + 3];
369                                 p [5] = value [startIndex + 2];
370                                 p [6] = value [startIndex + 1];
371                                 p [7] = value [startIndex + 0];
372
373                                 return ret;
374                         }
375
376                         PutBytes ((byte *) &ret, value, startIndex, 8);
377
378                         return ret;
379                 }
380                 
381                 public static string ToString (byte[] value)
382                 {
383                         if (value == null)
384                                 throw new ArgumentNullException ("value");
385
386                         return ToString (value, 0, value.Length);
387                 }
388
389                 public static string ToString (byte[] value, int startIndex)
390                 {
391                         if (value == null)
392                                 throw new ArgumentNullException ("value");
393
394                         return ToString (value, startIndex, value.Length - startIndex);
395                 }
396
397                 public static string ToString (byte[] value, int startIndex, int length)
398                 {
399                         if (value == null)
400                                 throw new ArgumentNullException ("byteArray");
401
402                         // The 4th and last clause (start_index >= value.Length)
403                         // was added as a small fix to a very obscure bug.
404                         // It makes a small difference when start_index is
405                         // outside the range and length==0. 
406                         if (startIndex < 0 || startIndex >= value.Length) {
407 #if NET_2_0
408                                 // special (but valid) case (e.g. new byte [0])
409                                 if ((startIndex == 0) && (value.Length == 0))
410                                         return String.Empty;
411 #endif
412                                 throw new ArgumentOutOfRangeException ("startIndex", "Index was"
413                                         + " out of range. Must be non-negative and less than the"
414                                         + " size of the collection.");
415                         }
416
417                         if (length < 0)
418                                 throw new ArgumentOutOfRangeException ("length",
419                                         "Value must be positive.");
420
421                         // note: re-ordered to avoid possible integer overflow
422                         if (startIndex > value.Length - length)
423                                 throw new ArgumentException ("startIndex + length > value.Length");
424
425                         if (length == 0)
426                                 return string.Empty;
427
428                         StringBuilder builder = new StringBuilder(length * 3 - 1);
429                         int end = startIndex + length;
430
431                         for (int i = startIndex; i < end; i++) {
432                                 if (i > startIndex)
433                                         builder.Append('-');
434                                 
435                                 char high = (char)((value[i] >> 4) & 0x0f);
436                                 char low = (char)(value[i] & 0x0f);
437
438                                 if (high < 10) 
439                                         high += '0';
440                                 else {
441                                         high -= (char) 10;
442                                         high += 'A';
443                                 }
444
445                                 if (low < 10)
446                                         low += '0';
447                                 else {
448                                         low -= (char) 10;
449                                         low += 'A';
450                                 }
451                                 builder.Append(high);
452                                 builder.Append(low);
453                         }
454
455                         return builder.ToString ();
456                 }
457         }
458 }