Merge pull request #2799 from BrzVlad/fix-conc-card-clean
[mono.git] / mcs / class / corlib / ReferenceSources / ParseNumbers.cs
1 //
2 // ParseNumbers.cs
3 //
4 // Authors:
5 //      Marek Safar  <marek.safar@gmail.com>
6 //
7 // Copyright (C) 2015 Xamarin Inc (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System.Globalization;
30 using System.Text;
31
32 namespace System {
33    
34     static class ParseNumbers
35     {
36         internal const int PrintAsI1=0x40;
37         internal const int PrintAsI2=0x80;
38 //        internal const int PrintAsI4=0x100;
39         internal const int TreatAsUnsigned=0x200;
40         internal const int TreatAsI1=0x400;
41         internal const int TreatAsI2=0x800;
42         internal const int IsTight=0x1000;
43         internal const int NoSpace=0x2000;
44
45                 public static int StringToInt (string value, int fromBase, int flags)
46                 {
47                         unsafe {
48                                 return StringToInt (value, fromBase, flags, null);
49                         }
50                 }
51
52                 public unsafe static int StringToInt (string value, int fromBase, int flags, int* parsePos)
53                 {
54                         if ((flags & (IsTight | NoSpace)) == 0)
55                                 throw new NotImplementedException (flags.ToString ());
56                         
57                         if (value == null)
58                                 return 0;
59
60                         int chars = 0;
61                         uint result = 0;
62                         int digitValue;
63
64                         int len = value.Length;
65                         bool negative = false;
66
67                         if (len == 0) {
68                                 // Mimic broken .net behaviour
69                                 throw new ArgumentOutOfRangeException ("Empty string");
70                         }
71
72                         int i = parsePos == null ? 0 : *parsePos;
73
74                         //Check for a sign
75                         if (value [i] == '-') {
76                                 if (fromBase != 10)
77                                         throw new ArgumentException ("String cannot contain a minus sign if the base is not 10.");
78
79                                 if ((flags & TreatAsUnsigned) != 0)
80                                         throw new OverflowException ("Negative number");
81
82                                 negative = true;
83                                 i++;
84                         } else if (value [i] == '+') {
85                                 i++;
86                         }
87
88                         if (fromBase == 16 && i + 1 < len && value [i] =='0' && (value [i + 1] == 'x' || value [i + 1] == 'X')) {
89                                 i += 2;
90                         }
91
92                         uint max_value;
93                         if ((flags & TreatAsI1) != 0) {
94                                 max_value = Byte.MaxValue;
95                         } else if ((flags & TreatAsI2) != 0) {
96                                 max_value = UInt16.MaxValue;
97                         } else {
98                                 max_value = UInt32.MaxValue;
99                         }
100
101                         while (i < len) {
102                                 char c = value [i];
103                                 if (Char.IsNumber (c)) {
104                                         digitValue = c - '0';
105                                 } else if (Char.IsLetter (c)) {
106                                         digitValue = Char.ToLowerInvariant (c) - 'a' + 10;
107                                 } else {
108                                         if (i == 0)
109                                                 throw new FormatException ("Could not find any parsable digits.");
110                                         
111                                         if ((flags & IsTight) != 0)
112                                                 throw new FormatException ("Additional unparsable characters are at the end of the string.");
113                                         
114                                         break;
115                                 }
116
117                                 if (digitValue >= fromBase) {
118                                         if (chars > 0) {
119                                                 throw new FormatException ("Additional unparsable characters are at the end of the string.");
120                                         }
121
122                                         throw new FormatException ("Could not find any parsable digits.");
123                                 }
124
125                                 long res = fromBase * result + digitValue;
126                                 if (res > max_value)
127                                         throw new OverflowException ();
128                                         
129                                 result = (uint)res;
130                                 chars++;
131                                 ++i;
132                         }
133
134                         if (chars == 0)
135                                 throw new FormatException ("Could not find any parsable digits.");
136
137                         if (parsePos != null)
138                                 *parsePos = i;
139
140                         return negative ? -(int)result : (int)result;
141                 }        
142
143                 public static string LongToString (long value, int toBase, int width, char paddingChar, int flags)
144         {
145                         if (value == 0)
146                                 return "0";
147                         if (toBase == 10)
148                                 return value.ToString ();
149
150                         byte[] val = BitConverter.GetBytes (value);
151
152                         switch (toBase) {
153                         case 2:
154                                 return ConvertToBase2 (val).ToString ();
155                         case 8:
156                                 return ConvertToBase8 (val).ToString ();
157                         case 16:
158                                 return ConvertToBase16 (val).ToString ();
159                         default:
160                                 throw new NotImplementedException ();
161                         }
162         }
163
164                 public static long StringToLong (string value, int fromBase, int flags)
165                 {
166                         unsafe {
167                                 return StringToLong (value, fromBase, flags, null);
168                         }
169                 }
170
171                 // Value from which a new base 16 digit can cause an overflow.
172                 const ulong base16MaxOverflowFreeValue = ulong.MaxValue / (16 * 16);
173
174                 // From ulong we can only cast to positive long.
175                 // As |long.MinValue| > |long.MaxValue| we need to do this to avoid an overflow.
176                 const ulong longMinValue = ((ulong) long.MaxValue) + (ulong) -(long.MinValue + long.MaxValue);
177
178                 public unsafe static long StringToLong (string value, int fromBase, int flags, int* parsePos)
179                 {
180                         if ((flags & (IsTight | NoSpace)) == 0)
181                                 throw new NotImplementedException (flags.ToString ());
182
183                         if (value == null)
184                                 return 0;
185
186                         int chars = 0;
187                         ulong fromBaseULong = (ulong) fromBase;
188                         ulong digitValue = 0;
189                         ulong result = 0;
190
191                         int len = value.Length;
192                         bool negative = false;
193                         bool treatAsUnsigned = (flags & ParseNumbers.TreatAsUnsigned) != 0;
194
195                         if (len == 0) {
196                                 // Mimic broken .net behaviour
197                                 throw new ArgumentOutOfRangeException ("Empty string");
198                         }
199
200                         int i = parsePos == null ? 0 : *parsePos;
201
202                         //Check for a sign
203                         if (value [i] == '-') {
204                                 if (fromBase != 10)
205                                         throw new ArgumentException ("String cannot contain a minus sign if the base is not 10.");
206
207                                 if (treatAsUnsigned)
208                                         throw new OverflowException ("Negative number");
209
210                                 negative = true;
211                                 i++;
212                         } else if (value [i] == '+') {
213                                 i++;
214                         }
215
216                         if (fromBase == 16 && i + 1 < len && value [i] =='0' && (value [i + 1] == 'x' || value [i + 1] == 'X')) {
217                                 i += 2;
218                         }
219
220                         while (i < len) {
221                                 char c = value[i];
222                                 if (Char.IsNumber (c)) {
223                                         digitValue = (ulong) (c - '0');
224                                 } else if (Char.IsLetter (c)) {
225                                         digitValue = (ulong) (Char.ToLowerInvariant (c) - 'a' + 10);
226                                 } else {
227                                         if (i == 0)
228                                                 throw new FormatException ("Could not find any parsable digits.");
229
230                                         if ((flags & IsTight) != 0)
231                                                 throw new FormatException ("Additional unparsable characters are at the end of the string.");
232
233                                         break;
234                                 }
235
236                                 if (digitValue >= fromBaseULong) {
237                                         if (chars > 0) {
238                                                 throw new FormatException ("Additional unparsable "
239                                                         + "characters are at the end of the string.");
240                                         } else {
241                                                 throw new FormatException ("Could not find any parsable"
242                                                         + " digits.");
243                                         }
244                                 }
245
246                                 if (result <= base16MaxOverflowFreeValue) {
247                                         result = result * (ulong) fromBaseULong + digitValue;
248                                 } else {
249                                         // decompose 64 bit operation into 32 bit operations so we can check for overflows
250                                         ulong a = (result >> 32) * fromBaseULong;
251                                         ulong b = (result & uint.MaxValue) * fromBaseULong + digitValue;
252                                         if (((b >> 32) + a) > uint.MaxValue)
253                                                 throw new OverflowException ();
254
255                                         result = (a << 32) + b;
256                                 }
257
258                                 chars++;
259                                 ++i;
260                         }
261
262                         if (chars == 0)
263                                 throw new FormatException ("Could not find any parsable digits.");
264
265                         if (parsePos != null)
266                                 *parsePos = i;
267
268                         if (treatAsUnsigned)
269                                 return (long) result;
270
271                         if (!negative) {
272                                 if (fromBase == 10 && result > ((ulong) long.MaxValue))
273                                         throw new OverflowException ();
274
275                                 return (long)result;
276                         }
277
278                         if (result <= (ulong) long.MaxValue)
279                                 return -((long) result);
280
281                         if (result > longMinValue)
282                                 throw new OverflowException ();
283
284                         // Avoids overflow of -result when result > long.MaxValue
285                         return long.MinValue + (long) (longMinValue - result);
286                 }
287
288                 public static string IntToString (int value, int toBase, int width, char paddingChar, int flags)
289                 {
290                         StringBuilder sb;
291
292                         if (value == 0) {
293                                 if (width <= 0)
294                                         return "0";
295
296                                 sb = new StringBuilder ("0", width);
297                         } else if (toBase == 10)
298                                 sb = new StringBuilder (value.ToString ());
299                         else {
300                                 byte[] val;
301                                 if ((flags & PrintAsI1) != 0) {
302                                         val = BitConverter.GetBytes ((byte) value);
303                                 } else if ((flags & PrintAsI2) != 0) {
304                                         val = BitConverter.GetBytes ((short) value);
305                                 } else {
306                                         val = BitConverter.GetBytes (value);
307                                 }
308
309                                 switch (toBase) {
310                                 case 2:
311                                         sb = ConvertToBase2 (val);
312                                         break;
313                                 case 8:
314                                         sb = ConvertToBase8 (val);
315                                         break;
316                                 case 16:
317                                         sb = ConvertToBase16 (val);
318                                         break;
319                                 default:
320                                         throw new NotImplementedException ();
321                                 }
322                         }
323
324                         var padding = width - sb.Length;
325                         while (padding > 0) {
326                                 sb.Insert (0, paddingChar);
327                                 --padding;
328                         }
329
330                         return sb.ToString ();
331                 }
332
333                 static void EndianSwap (ref byte[] value)
334                 {
335                         byte[] buf = new byte[value.Length];
336                         for (int i = 0; i < value.Length; i++)
337                                 buf[i] = value[value.Length-1-i];
338                         value = buf;
339                 }
340
341                 static StringBuilder ConvertToBase2 (byte[] value)
342                 {
343                         if (!BitConverter.IsLittleEndian)
344                                 EndianSwap (ref value);
345                         StringBuilder sb = new StringBuilder ();
346                         for (int i = value.Length - 1; i >= 0; i--) {
347                                 byte b = value [i];
348                                 for (int j = 0; j < 8; j++) {
349                                         if ((b & 0x80) == 0x80) {
350                                                 sb.Append ('1');
351                                         }
352                                         else {
353                                                 if (sb.Length > 0)
354                                                         sb.Append ('0');
355                                         }
356                                         b <<= 1;
357                                 }
358                         }
359                         return sb;
360                 }
361
362                 static StringBuilder ConvertToBase8 (byte[] value)
363                 {
364                         ulong l = 0;
365                         switch (value.Length) {
366                         case 1:
367                                 l = (ulong) value [0];
368                                 break;
369                         case 2:
370                                 l = (ulong) BitConverter.ToUInt16 (value, 0);
371                                 break;
372                         case 4:
373                                 l = (ulong) BitConverter.ToUInt32 (value, 0);
374                                 break;
375                         case 8:
376                                 l = BitConverter.ToUInt64 (value, 0);
377                                 break;
378                         default:
379                                 throw new ArgumentException ("value");
380                         }
381
382                         StringBuilder sb = new StringBuilder ();
383                         for (int i = 21; i >= 0; i--) {
384                                 // 3 bits at the time
385                                 char val = (char) ((l >> i * 3) & 0x7);
386                                 if ((val != 0) || (sb.Length > 0)) {
387                                         val += '0';
388                                         sb.Append (val);
389                                 }
390                         }
391                         return sb;
392                 }
393
394                 static StringBuilder ConvertToBase16 (byte[] value)
395                 {
396                         if (!BitConverter.IsLittleEndian)
397                                 EndianSwap (ref value);
398                         StringBuilder sb = new StringBuilder ();
399                         for (int i = value.Length - 1; i >= 0; i--) {
400                                 char high = (char)((value[i] >> 4) & 0x0f);
401                                 if ((high != 0) || (sb.Length > 0)) {
402                                         if (high < 10) 
403                                                 high += '0';
404                                         else {
405                                                 high -= (char) 10;
406                                                 high += 'a';
407                                         }
408                                         sb.Append (high);
409                                 }
410
411                                 char low = (char)(value[i] & 0x0f);
412                                 if ((low != 0) || (sb.Length > 0)) {
413                                         if (low < 10)
414                                                 low += '0';
415                                         else {
416                                                 low -= (char) 10;
417                                                 low += 'a';
418                                         }
419                                         sb.Append (low);
420                                 }
421                         }
422                         return sb;
423                 }
424
425     }
426 }