Fix to UriTemplate.Match to properly handle query parameters without a value. No...
[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                         if ((flags & IsTight) == 0)
48                                 throw new NotImplementedException ();
49                         
50                         if (value == null)
51                                 return 0;
52
53                         int chars = 0;
54                         uint result = 0;
55                         int digitValue;
56
57                         int i = 0; 
58                         int len = value.Length;
59                         bool negative = false;
60
61                         if (len == 0) {
62                                 // Mimic broken .net behaviour
63                                 throw new ArgumentOutOfRangeException ("Empty string");
64                         }
65
66                         //Check for a sign
67                         if (value [i] == '-') {
68                                 if (fromBase != 10)
69                                         throw new ArgumentException ("String cannot contain a minus sign if the base is not 10.");
70
71                                 if ((flags & TreatAsUnsigned) != 0)
72                                         throw new OverflowException ("Negative number");
73
74                                 negative = true;
75                                 i++;
76                         } else if (value [i] == '+') {
77                                 i++;
78                         }
79
80                         if (fromBase == 16 && i + 1 < len && value [i] =='0' && (value [i + 1] == 'x' || value [i + 1] == 'X')) {
81                                 i += 2;
82                         }
83
84                         uint max_value;
85                         if ((flags & TreatAsI1) != 0) {
86                                 max_value = Byte.MaxValue;
87                         } else if ((flags & TreatAsI2) != 0) {
88                                 max_value = UInt16.MaxValue;
89                         } else {
90                                 max_value = UInt32.MaxValue;
91                         }
92
93                         while (i < len) {
94                                 char c = value [i++];
95                                 if (Char.IsNumber (c)) {
96                                         digitValue = c - '0';
97                                 } else if (Char.IsLetter (c)) {
98                                         digitValue = Char.ToLowerInvariant (c) - 'a' + 10;
99                                 } else {
100                                         if (chars > 0) {
101                                                 throw new FormatException ("Additional unparsable characters are at the end of the string.");
102                                         }
103                                         
104                                         throw new FormatException ("Could not find any parsable digits.");
105                                 }
106
107                                 if (digitValue >= fromBase) {
108                                         if (chars > 0) {
109                                                 throw new FormatException ("Additional unparsable characters are at the end of the string.");
110                                         }
111
112                                         throw new FormatException ("Could not find any parsable digits.");
113                                 }
114
115                                 var res = (uint) fromBase * result + (uint) digitValue;
116                                 if (res < result || res > max_value)
117                                         throw new OverflowException ();
118                                         
119                                 result = res;
120                                 chars++;
121                         }
122
123                         if (chars == 0)
124                                 throw new FormatException ("Could not find any parsable digits.");
125
126                         return negative ? -(int)result : (int)result;
127                 }        
128
129                 public static string LongToString (long value, int toBase, int width, char paddingChar, int flags)
130         {
131                         if (value == 0)
132                                 return "0";
133                         if (toBase == 10)
134                                 return value.ToString ();
135
136                         byte[] val = BitConverter.GetBytes (value);
137
138                         switch (toBase) {
139                         case 2:
140                                 return ConvertToBase2 (val);
141                         case 8:
142                                 return ConvertToBase8 (val);
143                         case 16:
144                                 return ConvertToBase16 (val);
145                         default:
146                                 throw new NotImplementedException ();
147                         }
148         }
149
150                 public static long StringToLong (string value, int fromBase, int flags)
151                 {
152                         if ((flags & IsTight) == 0)
153                                 throw new NotImplementedException ();
154
155                         if (value == null)
156                                 return 0;
157
158                         int chars = 0;
159                         int digitValue = -1;
160                         long result = 0;
161
162                         int i = 0; 
163                         int len = value.Length;
164                         bool negative = false;
165
166                         if (len == 0) {
167                                 // Mimic broken .net behaviour
168                                 throw new ArgumentOutOfRangeException ("Empty string");
169                         }
170
171                         //Check for a sign
172                         if (value [i] == '-') {
173                                 if (fromBase != 10)
174                                         throw new ArgumentException ("String cannot contain a minus sign if the base is not 10.");
175
176                                 if ((flags & TreatAsUnsigned) != 0)
177                                         throw new OverflowException ("Negative number");
178
179                                 negative = true;
180                                 i++;
181                         } else if (value [i] == '+') {
182                                 i++;
183                         }
184
185                         if (fromBase == 16 && i + 1 < len && value [i] =='0' && (value [i + 1] == 'x' || value [i + 1] == 'X')) {
186                                 i += 2;
187                         }
188
189                         while (i < len) {
190                                 char c = value[i++];
191                                 if (Char.IsNumber (c)) {
192                                         digitValue = c - '0';
193                                 } else if (Char.IsLetter (c)) {
194                                         digitValue = Char.ToLowerInvariant (c) - 'a' + 10;
195                                 } else {
196                                         if (chars > 0) {
197                                                 throw new FormatException ("Additional unparsable "
198                                                         + "characters are at the end of the string.");
199                                         } else {
200                                                 throw new FormatException ("Could not find any parsable"
201                                                         + " digits.");
202                                         }
203                                 }
204
205                                 if (digitValue >= fromBase) {
206                                         if (chars > 0) {
207                                                 throw new FormatException ("Additional unparsable "
208                                                         + "characters are at the end of the string.");
209                                         } else {
210                                                 throw new FormatException ("Could not find any parsable"
211                                                         + " digits.");
212                                         }
213                                 }
214
215                                 result = fromBase * result + digitValue;
216                                 chars++;
217                         }
218
219                         if (chars == 0)
220                                 throw new FormatException ("Could not find any parsable digits.");
221
222                         return negative ? -result : result;
223                 }
224
225                 public static string IntToString (int value, int toBase, int width, char paddingChar, int flags)
226                 {
227                         if (value == 0)
228                                 return "0";
229
230                         if (toBase == 10)
231                                 return value.ToString ();
232
233                         byte[] val;
234                         if ((flags & PrintAsI1) != 0) {
235                                 val = BitConverter.GetBytes ((byte) value);
236                         } else if ((flags & PrintAsI2) != 0) {
237                                 val = BitConverter.GetBytes ((short) value);
238                         } else {
239                                 val = BitConverter.GetBytes (value);
240                         }
241
242                         switch (toBase) {
243                         case 2:
244                                 return ConvertToBase2 (val);
245                         case 8:
246                                 return ConvertToBase8 (val);
247                         case 16:
248                                 return ConvertToBase16 (val);
249                         default:
250                                 throw new NotImplementedException ();
251                         }
252                 }
253
254                 static void EndianSwap (ref byte[] value)
255                 {
256                         byte[] buf = new byte[value.Length];
257                         for (int i = 0; i < value.Length; i++)
258                                 buf[i] = value[value.Length-1-i];
259                         value = buf;
260                 }
261
262                 static string ConvertToBase2 (byte[] value)
263                 {
264                         if (!BitConverter.IsLittleEndian)
265                                 EndianSwap (ref value);
266                         StringBuilder sb = new StringBuilder ();
267                         for (int i = value.Length - 1; i >= 0; i--) {
268                                 byte b = value [i];
269                                 for (int j = 0; j < 8; j++) {
270                                         if ((b & 0x80) == 0x80) {
271                                                 sb.Append ('1');
272                                         }
273                                         else {
274                                                 if (sb.Length > 0)
275                                                         sb.Append ('0');
276                                         }
277                                         b <<= 1;
278                                 }
279                         }
280                         return sb.ToString ();
281                 }
282
283                 static string ConvertToBase8 (byte[] value)
284                 {
285                         ulong l = 0;
286                         switch (value.Length) {
287                         case 1:
288                                 l = (ulong) value [0];
289                                 break;
290                         case 2:
291                                 l = (ulong) BitConverter.ToUInt16 (value, 0);
292                                 break;
293                         case 4:
294                                 l = (ulong) BitConverter.ToUInt32 (value, 0);
295                                 break;
296                         case 8:
297                                 l = BitConverter.ToUInt64 (value, 0);
298                                 break;
299                         default:
300                                 throw new ArgumentException ("value");
301                         }
302
303                         StringBuilder sb = new StringBuilder ();
304                         for (int i = 21; i >= 0; i--) {
305                                 // 3 bits at the time
306                                 char val = (char) ((l >> i * 3) & 0x7);
307                                 if ((val != 0) || (sb.Length > 0)) {
308                                         val += '0';
309                                         sb.Append (val);
310                                 }
311                         }
312                         return sb.ToString ();
313                 }
314
315                 static string ConvertToBase16 (byte[] value)
316                 {
317                         if (!BitConverter.IsLittleEndian)
318                                 EndianSwap (ref value);
319                         StringBuilder sb = new StringBuilder ();
320                         for (int i = value.Length - 1; i >= 0; i--) {
321                                 char high = (char)((value[i] >> 4) & 0x0f);
322                                 if ((high != 0) || (sb.Length > 0)) {
323                                         if (high < 10) 
324                                                 high += '0';
325                                         else {
326                                                 high -= (char) 10;
327                                                 high += 'a';
328                                         }
329                                         sb.Append (high);
330                                 }
331
332                                 char low = (char)(value[i] & 0x0f);
333                                 if ((low != 0) || (sb.Length > 0)) {
334                                         if (low < 10)
335                                                 low += '0';
336                                         else {
337                                                 low -= (char) 10;
338                                                 low += 'a';
339                                         }
340                                         sb.Append (low);
341                                 }
342                         }
343                         return sb.ToString ();
344                 }
345
346     }
347 }