5 // Marek Safar <marek.safar@gmail.com>
7 // Copyright (C) 2015 Xamarin Inc (http://www.xamarin.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
29 using System.Globalization;
34 static class ParseNumbers
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;
45 public static int StringToInt (string value, int fromBase, int flags)
48 return StringToInt (value, fromBase, flags, null);
52 public unsafe static int StringToInt (string value, int fromBase, int flags, int* parsePos)
54 if ((flags & (IsTight | NoSpace)) == 0)
55 throw new NotImplementedException (flags.ToString ());
64 int len = value.Length;
65 bool negative = false;
68 // Mimic broken .net behaviour
69 throw new ArgumentOutOfRangeException ("Empty string");
72 int i = parsePos == null ? 0 : *parsePos;
75 if (value [i] == '-') {
77 throw new ArgumentException ("String cannot contain a minus sign if the base is not 10.");
79 if ((flags & TreatAsUnsigned) != 0)
80 throw new OverflowException ("Negative number");
84 } else if (value [i] == '+') {
88 if (fromBase == 16 && i + 1 < len && value [i] =='0' && (value [i + 1] == 'x' || value [i + 1] == 'X')) {
93 if ((flags & TreatAsI1) != 0) {
94 max_value = Byte.MaxValue;
95 } else if ((flags & TreatAsI2) != 0) {
96 max_value = UInt16.MaxValue;
98 max_value = UInt32.MaxValue;
103 if (Char.IsNumber (c)) {
104 digitValue = c - '0';
105 } else if (Char.IsLetter (c)) {
106 digitValue = Char.ToLowerInvariant (c) - 'a' + 10;
109 throw new FormatException ("Could not find any parsable digits.");
111 if ((flags & IsTight) != 0)
112 throw new FormatException ("Additional unparsable characters are at the end of the string.");
117 if (digitValue >= fromBase) {
119 throw new FormatException ("Additional unparsable characters are at the end of the string.");
122 throw new FormatException ("Could not find any parsable digits.");
125 long res = fromBase * result + digitValue;
127 throw new OverflowException ();
135 throw new FormatException ("Could not find any parsable digits.");
137 if (parsePos != null)
140 return negative ? -(int)result : (int)result;
143 public static string LongToString (long value, int toBase, int width, char paddingChar, int flags)
148 return value.ToString ();
150 byte[] val = BitConverter.GetBytes (value);
154 return ConvertToBase2 (val).ToString ();
156 return ConvertToBase8 (val).ToString ();
158 return ConvertToBase16 (val).ToString ();
160 throw new NotImplementedException ();
164 public static long StringToLong (string value, int fromBase, int flags)
167 return StringToLong (value, fromBase, flags, null);
171 // Value from which a new base 16 digit can cause an overflow.
172 const ulong base16MaxOverflowFreeValue = ulong.MaxValue / (16 * 16);
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);
178 public unsafe static long StringToLong (string value, int fromBase, int flags, int* parsePos)
180 if ((flags & (IsTight | NoSpace)) == 0)
181 throw new NotImplementedException (flags.ToString ());
187 ulong fromBaseULong = (ulong) fromBase;
188 ulong digitValue = 0;
191 int len = value.Length;
192 bool negative = false;
193 bool treatAsUnsigned = (flags & ParseNumbers.TreatAsUnsigned) != 0;
196 // Mimic broken .net behaviour
197 throw new ArgumentOutOfRangeException ("Empty string");
200 int i = parsePos == null ? 0 : *parsePos;
203 if (value [i] == '-') {
205 throw new ArgumentException ("String cannot contain a minus sign if the base is not 10.");
208 throw new OverflowException ("Negative number");
212 } else if (value [i] == '+') {
216 if (fromBase == 16 && i + 1 < len && value [i] =='0' && (value [i + 1] == 'x' || value [i + 1] == 'X')) {
222 if (Char.IsNumber (c)) {
223 digitValue = (ulong) (c - '0');
224 } else if (Char.IsLetter (c)) {
225 digitValue = (ulong) (Char.ToLowerInvariant (c) - 'a' + 10);
228 throw new FormatException ("Could not find any parsable digits.");
230 if ((flags & IsTight) != 0)
231 throw new FormatException ("Additional unparsable characters are at the end of the string.");
236 if (digitValue >= fromBaseULong) {
238 throw new FormatException ("Additional unparsable "
239 + "characters are at the end of the string.");
241 throw new FormatException ("Could not find any parsable"
246 if (result <= base16MaxOverflowFreeValue) {
247 result = result * (ulong) fromBaseULong + digitValue;
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 ();
255 result = (a << 32) + b;
263 throw new FormatException ("Could not find any parsable digits.");
265 if (parsePos != null)
269 return (long) result;
272 if (fromBase == 10 && result > ((ulong) long.MaxValue))
273 throw new OverflowException ();
278 if (result <= (ulong) long.MaxValue)
279 return -((long) result);
281 if (result > longMinValue)
282 throw new OverflowException ();
284 // Avoids overflow of -result when result > long.MaxValue
285 return long.MinValue + (long) (longMinValue - result);
288 public static string IntToString (int value, int toBase, int width, char paddingChar, int flags)
296 sb = new StringBuilder ("0", width);
297 } else if (toBase == 10)
298 sb = new StringBuilder (value.ToString ());
301 if ((flags & PrintAsI1) != 0) {
302 val = BitConverter.GetBytes ((byte) value);
303 } else if ((flags & PrintAsI2) != 0) {
304 val = BitConverter.GetBytes ((short) value);
306 val = BitConverter.GetBytes (value);
311 sb = ConvertToBase2 (val);
314 sb = ConvertToBase8 (val);
317 sb = ConvertToBase16 (val);
320 throw new NotImplementedException ();
324 var padding = width - sb.Length;
325 while (padding > 0) {
326 sb.Insert (0, paddingChar);
330 return sb.ToString ();
333 static void EndianSwap (ref byte[] value)
335 byte[] buf = new byte[value.Length];
336 for (int i = 0; i < value.Length; i++)
337 buf[i] = value[value.Length-1-i];
341 static StringBuilder ConvertToBase2 (byte[] value)
343 if (!BitConverter.IsLittleEndian)
344 EndianSwap (ref value);
345 StringBuilder sb = new StringBuilder ();
346 for (int i = value.Length - 1; i >= 0; i--) {
348 for (int j = 0; j < 8; j++) {
349 if ((b & 0x80) == 0x80) {
362 static StringBuilder ConvertToBase8 (byte[] value)
365 switch (value.Length) {
367 l = (ulong) value [0];
370 l = (ulong) BitConverter.ToUInt16 (value, 0);
373 l = (ulong) BitConverter.ToUInt32 (value, 0);
376 l = BitConverter.ToUInt64 (value, 0);
379 throw new ArgumentException ("value");
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)) {
394 static StringBuilder ConvertToBase16 (byte[] value)
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)) {
411 char low = (char)(value[i] & 0x0f);
412 if ((low != 0) || (sb.Length > 0)) {