582 lines
22 KiB
C#
582 lines
22 KiB
C#
// ==++==
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// ==--==
|
|
/*============================================================
|
|
**
|
|
** Class: ParseNumbers
|
|
**
|
|
**
|
|
** Purpose: Methods for Parsing numbers and Strings.
|
|
** All methods are implemented in native.
|
|
**
|
|
** Date: April 7, 1998
|
|
**
|
|
===========================================================*/
|
|
namespace System {
|
|
|
|
using System;
|
|
using System.Runtime.CompilerServices;
|
|
internal class ParseNumbers {
|
|
internal const int PrintAsI1=0x40;
|
|
internal const int PrintAsI2=0x80;
|
|
internal const int PrintAsI4=0x100;
|
|
internal const int TreatAsUnsigned=0x200;
|
|
internal const int TreatAsI1=0x400;
|
|
internal const int TreatAsI2=0x800;
|
|
internal const int IsTight=0x1000;
|
|
internal static readonly int[] ZeroStart = {0};
|
|
|
|
//
|
|
// NATIVE METHODS
|
|
// For comments on these methods please see $\src\vm\COMUtilNative.cpp
|
|
//
|
|
public static long StringToLong(System.String s, int radix, int flags) {
|
|
int [] zeroStart = {0};
|
|
return StringToLong(s,radix,flags,zeroStart);
|
|
}
|
|
|
|
public static int StringToInt(System.String s, int radix, int flags) {
|
|
int [] zeroStart = {0};
|
|
return StringToInt(s,radix,flags,zeroStart);
|
|
}
|
|
|
|
// From Lightning\Src\VM\COMUtilNative.cpp:: enum FmtFlags
|
|
internal const int LeftAlign = 0x1;
|
|
internal const int CenterAlign = 0x2;
|
|
internal const int RightAlign = 0x4;
|
|
internal const int PrefixSpace = 0x8;
|
|
internal const int PrintSign = 0x10;
|
|
internal const int PrintBase = 0x20;
|
|
//internal const int TreatAsUnsigned = 0x10;
|
|
//internal const int PrintAsI1 = 0x40;
|
|
//internal const int PrintAsI2 = 0x80;
|
|
//internal const int PrintAsI4 = 0x100;
|
|
internal const int PrintRadixBase = 0x200;
|
|
internal const int AlternateForm = 0x400;
|
|
|
|
internal const int MinRadix=2;
|
|
internal const int MaxRadix=36;
|
|
|
|
static private int EatWhiteSpace(String buffer, int length, int i) {
|
|
for (; i<length && Char.IsWhiteSpace(buffer[i]); i++);
|
|
return i;
|
|
}
|
|
|
|
static private uint GrabInts(uint radix, String buffer, int length,
|
|
ref int i,bool isUnsigned) {
|
|
uint result=0;
|
|
uint value;
|
|
uint maxVal;
|
|
#if DEBUG
|
|
if (!(radix==2 || radix==8 || radix==10 || radix==16)) {
|
|
VTable.DebugBreak();
|
|
}
|
|
#endif
|
|
// Allow all non-decimal numbers to set the sign bit.
|
|
if (radix==10 && !isUnsigned) {
|
|
maxVal = (0x7FFFFFFF / 10);
|
|
while (i < length && IsDigit(buffer[i], radix, out value)) {
|
|
// Read all of the digits and convert to a number
|
|
// Check for overflows - this is sufficient & correct.
|
|
if (result > maxVal || ((int)result)<0) {
|
|
throw new OverflowException("Overflow_Int32");
|
|
}
|
|
result = result * radix + value;
|
|
i++;
|
|
}
|
|
if ((int)result<0 && result!=0x80000000) {
|
|
throw new OverflowException("Overflow_Int32");
|
|
}
|
|
} else {
|
|
maxVal = UInt32.MaxValue / radix;
|
|
while (i < length && IsDigit(buffer[i], radix, out value)) {
|
|
// Read all of the digits and convert to a number
|
|
// Check for overflows - this is sufficient & correct.
|
|
if (result > maxVal) {
|
|
throw new OverflowException("Overflow_UInt32");
|
|
}
|
|
result = result * radix + value;
|
|
i++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static private ulong GrabLongs(uint radix, String buffer, int length,
|
|
ref int i, bool isUnsigned) {
|
|
ulong result = 0;
|
|
uint value;
|
|
ulong maxVal;
|
|
// Allow all non-decimal numbers to set the sign bit.
|
|
if (radix == 10 && !isUnsigned) {
|
|
maxVal = (0x7FFFFFFFFFFFFFFF / 10);
|
|
while (i < length && IsDigit(buffer[i], radix, out value)) {
|
|
// Read all of the digits and convert to a number
|
|
// Check for overflows - this is sufficient & correct.
|
|
if (result > maxVal || ((long) result) < 0) {
|
|
throw new OverflowException("Overflow_Int64");
|
|
}
|
|
result = result * radix + value;
|
|
i++;
|
|
}
|
|
if ((long) result < 0 && result != 0x8000000000000000) {
|
|
throw new OverflowException("Overflow_Int64");
|
|
}
|
|
} else {
|
|
maxVal = unchecked((ulong) -1L) / radix;
|
|
while (i < length && IsDigit(buffer[i], radix, out value)) {
|
|
// Read all of the digits and convert to a number
|
|
// Check for overflows - this is sufficient & correct.
|
|
if (result > maxVal) {
|
|
throw new OverflowException("Overflow_Int64");
|
|
}
|
|
result = result * radix + value;
|
|
i++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static bool IsDigit(char c, uint radix, out uint result) {
|
|
if (c>='0' && c<='9') {
|
|
result = (uint) (c-'0');
|
|
} else {
|
|
char d = Char.ToLower(c);
|
|
if (d>='a' && d<='z') {
|
|
//+10 is necessary because a is actually 10, etc.
|
|
result = (uint) (d-'a'+10);
|
|
} else {
|
|
result = 0;
|
|
return false;
|
|
}
|
|
}
|
|
return (result < radix);
|
|
}
|
|
|
|
public static int StringToInt(String s, int radix, int flags,
|
|
int[] currPos)
|
|
{
|
|
if (s == null) {
|
|
return 0;
|
|
}
|
|
int i = currPos[0];
|
|
//Do some radix checking.
|
|
//A radix of -1 says to use whatever base is spec'd on the number.
|
|
//Parse in Base10 until we figure out what the base actually is.
|
|
uint r = (-1==radix)?10U:(uint)radix;
|
|
if (r!=2 && r!=10 && r!=8 && r!=16) {
|
|
throw new ArgumentException("Arg_InvalidBase");
|
|
}
|
|
int length = s.Length;
|
|
if (i <0 || i>=length) {
|
|
throw new ArgumentOutOfRangeException("startIndex(currPos[0])");
|
|
}
|
|
// Get rid of the whitespace and then check that we've still got
|
|
// some digits to parse.
|
|
if ((flags & IsTight) != 0) {
|
|
i = EatWhiteSpace(s,length,i);
|
|
if (i==length) {
|
|
throw new FormatException("Format_EmptyInputString");
|
|
}
|
|
}
|
|
int sign = 1;
|
|
bool isUnsigned = ((flags & TreatAsUnsigned) != 0);
|
|
if (s[i]=='-') { // Check for a sign
|
|
if (r != 10) {
|
|
throw new ArgumentException("ArgCannotHaveNegativeValue");
|
|
}
|
|
if (isUnsigned) {
|
|
throw new OverflowException("Overflow_NegativeUnsigned");
|
|
}
|
|
sign = -1;
|
|
i++;
|
|
} else if (s[i]=='+') {
|
|
i++;
|
|
}
|
|
//Consume the 0x if we're in an unknown base or in base 16.
|
|
if ((radix==-1||radix==16) && (i+1<length) && s[i]=='0') {
|
|
if (s[i+1]=='x' || s[i+1]=='X') {
|
|
r=16;
|
|
i+=2;
|
|
}
|
|
}
|
|
int grabNumbersStart=i;
|
|
uint result = GrabInts(r,s,length,ref i, isUnsigned);
|
|
if (i==grabNumbersStart) {
|
|
throw new FormatException("Format_NoParsibleDigits");
|
|
}
|
|
if ((flags & IsTight) != 0) {
|
|
// i = EatWhiteSpace(s,length,i);
|
|
//If we've got effluvia left at the end of the string, complain.
|
|
if (i<(length)) {
|
|
throw new FormatException("Format_ExtraJunkAtEnd");
|
|
}
|
|
}
|
|
//Put the current index back into the correct place.
|
|
currPos[0]=i;
|
|
//Return the value properly signed.
|
|
if ((flags & TreatAsI1) != 0) {
|
|
if (result > 0xFF) {
|
|
throw new OverflowException("Overflow_SByte");
|
|
}
|
|
// _ASSERTE(sign==1 || r==10); // result looks positive when parsed as an I4
|
|
if (result >= 0x80){
|
|
sign = -1;
|
|
}
|
|
} else if ((flags & TreatAsI2) != 0) {
|
|
if (result > 0xFFFF) {
|
|
throw new OverflowException("Overflow_Int16");
|
|
}
|
|
// _ASSERTE(sign==1 || r==10); // result looks positive when parsed as an I4
|
|
if (result >= 0x8000) {
|
|
sign = -1;
|
|
}
|
|
} else if (result == 0x80000000 && sign==1 && r==10) {
|
|
throw new OverflowException("Overflow_Int32");
|
|
}
|
|
if (r == 10) {
|
|
return unchecked((int)result) * sign;
|
|
} else {
|
|
return unchecked((int) result);
|
|
}
|
|
}
|
|
|
|
public static long StringToLong(String s, int radix, int flags, int[] currPos) {
|
|
if (s == null) {
|
|
return 0L;
|
|
}
|
|
int i = currPos[0];
|
|
//Do some radix checking.
|
|
//A radix of -1 says to use whatever base is spec'd on the number.
|
|
//Parse in Base10 until we figure out what the base actually is.
|
|
uint r = (-1==radix)?10U:(uint)radix;
|
|
if (r!=2 && r!=10 && r!=8 && r!=16) {
|
|
throw new ArgumentException("Arg_InvalidBase");
|
|
}
|
|
int length = s.Length;
|
|
if (i < 0 || i >= length) {
|
|
throw new ArgumentOutOfRangeException("startIndex(currPos[0])");
|
|
}
|
|
// Get rid of the whitespace and then check that we've still
|
|
// got some digits to parse.
|
|
if ((flags & IsTight) != 0) {
|
|
i = EatWhiteSpace(s,length,i);
|
|
if (i == length) {
|
|
throw new FormatException("EmptyInputString");
|
|
}
|
|
}
|
|
int sign = 1;
|
|
bool isUnsigned = ((flags & TreatAsUnsigned) != 0);
|
|
if (s[i] == '-') {
|
|
if (r != 10) {
|
|
throw new ArgumentException("Negative non-base-10 number");
|
|
}
|
|
if (isUnsigned) {
|
|
throw new OverflowException("Negative unsigned number");
|
|
}
|
|
sign = -1;
|
|
i++;
|
|
} else if (s[i] == '+') {
|
|
i++;
|
|
}
|
|
//Consume the 0x if we're in an unknown base or in base 16.
|
|
if ((radix==-1||radix==16) && (i+1<length) && s[i]=='0') {
|
|
if (s[i+1]=='x' || s[i+1]=='X') {
|
|
r=16;
|
|
i+=2;
|
|
}
|
|
}
|
|
int grabNumbersStart = i;
|
|
ulong result = GrabLongs(r,s,length,ref i,isUnsigned);
|
|
if (i == grabNumbersStart) {
|
|
throw new FormatException("No parsable digits");
|
|
}
|
|
if ((flags & IsTight) != 0) {
|
|
if (i < length) {
|
|
throw new FormatException("Extra junk at end");
|
|
}
|
|
}
|
|
//Put the current index back into the correct place.
|
|
currPos[0]=i;
|
|
//Return the value properly signed.
|
|
if (result == 0x8000000000000000 && sign==1 && r==10) {
|
|
throw new OverflowException("Overflow_Int64");
|
|
}
|
|
if (r == 10) {
|
|
return unchecked((long) result) * sign;
|
|
} else {
|
|
return unchecked((long) result);
|
|
}
|
|
}
|
|
|
|
public static String IntToDecimalString(int i) {
|
|
// rusa: see also Lightning\Src\VM\COMUtilNative.cpp::IntToDecimalString
|
|
int count;
|
|
char[] buffer = IntToDecimalChars(i, out count);
|
|
return String.StringCTOR(buffer, 0, count);
|
|
}
|
|
|
|
internal static char[] IntToDecimalChars(int value, out int count) {
|
|
char[] result = new char[66];
|
|
bool isNegative;
|
|
uint uValue;
|
|
if (value == 0) {
|
|
result[0] = '0';
|
|
count = 1;
|
|
return result;
|
|
}
|
|
if (value < 0) {
|
|
isNegative = true;
|
|
uValue = (uint) -value;
|
|
} else {
|
|
isNegative = false;
|
|
uValue = (uint) value;
|
|
}
|
|
int index = 0;
|
|
do {
|
|
result[index] = (char) ((uValue % 10) + '0');
|
|
index++;
|
|
uValue = uValue / 10;
|
|
} while (uValue != 0);
|
|
if (isNegative) {
|
|
result[index] = '-';
|
|
index++;
|
|
}
|
|
// Reverse the characters in the buffer
|
|
int limit = index / 2;
|
|
count = limit;
|
|
int i = 0;
|
|
index--;
|
|
while (i < index) {
|
|
char t = result[i];
|
|
result[i] = result[index];
|
|
result[index] = t;
|
|
i++;
|
|
index--;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//FCIMPL5(LPVOID, ParseNumbers::IntToString, INT32 n, INT32 radix,
|
|
// INT32 width, WCHAR paddingChar, INT32 flags);
|
|
public static String IntToString(int n, int radix, int width,
|
|
char paddingChar, int flags) {
|
|
// rusa: see also Lightning\Src\VM\COMUtilNative.cpp::IntToString
|
|
|
|
// changed to fill the char[] backwards to call a normal
|
|
// stringCTOR and not have an additional copy
|
|
|
|
bool isNegative = false;
|
|
int charVal;
|
|
int buffLength;
|
|
uint l;
|
|
|
|
// Longest possible string length for an integer in
|
|
// binary notation with prefix
|
|
int bufSize = 66;
|
|
char[] buffer = new char[bufSize];
|
|
int index=bufSize;
|
|
|
|
if (radix<MinRadix || radix>MaxRadix) {
|
|
throw new ArgumentException("Argument_InvalidBase");
|
|
//COMPlusThrowArgumentException(L"radix", L"Arg_InvalidBase");
|
|
}
|
|
|
|
//If the number is negative, make it positive and remember the sign.
|
|
//If the number is MIN_VALUE, this will still be negative,
|
|
//so we'll have to special case this later.
|
|
if (n<0) {
|
|
isNegative=true;
|
|
// For base 10, write out -num, but other bases write out the
|
|
// 2's complement bit pattern
|
|
if (10==radix)
|
|
l = (uint)(-n);
|
|
else
|
|
l = (uint)n; // REVIEW: markples: comment suggests ~n wanted
|
|
} else {
|
|
l=(uint)n;
|
|
}
|
|
|
|
//The conversion to a UINT will sign extend the number. In order to
|
|
//ensure that we only get as many bits as we expect, we chop the
|
|
//number.
|
|
if ((flags & PrintAsI1) != 0) {
|
|
l = l&0xFF;
|
|
} else if ((flags & PrintAsI2) != 0) {
|
|
l = l&0xFFFF;
|
|
} else if ((flags & PrintAsI4) != 0) {
|
|
l=l&0xFFFFFFFF;
|
|
}
|
|
|
|
if (0==l) { //Special case the 0.
|
|
buffer[--index]='0';
|
|
} else {
|
|
do {
|
|
charVal = (int) (l%(uint)radix);
|
|
l=l/(uint)radix;
|
|
if (charVal<10) {
|
|
buffer[--index] = (char)(charVal + '0');
|
|
} else {
|
|
buffer[--index] = (char)(charVal + 'a' - 10);
|
|
}
|
|
} while (l!=0);
|
|
}
|
|
//If they want the base, append that to the string (in reverse order)
|
|
if (radix!=10 && ((flags & PrintBase)!=0)) {
|
|
if (16==radix) {
|
|
buffer[--index]='x';
|
|
buffer[--index]='0';
|
|
} else if (8==radix) {
|
|
buffer[--index]='0';
|
|
}
|
|
}
|
|
|
|
if (10==radix) {
|
|
if (isNegative) {
|
|
//If it was negative, append the sign.
|
|
buffer[--index]='-';
|
|
} else if ((flags&PrintSign)!=0) {
|
|
//else if they requested, add the '+';
|
|
buffer[--index]='+';
|
|
} else if ((flags&PrefixSpace)!=0) {
|
|
//If they requested a leading space, put it on.
|
|
buffer[--index]=' ';
|
|
}
|
|
}
|
|
|
|
//Figure out the size of our string.
|
|
if (width<=bufSize-index) {
|
|
buffLength=bufSize-index;
|
|
} else {
|
|
buffLength=width;
|
|
}
|
|
|
|
String local = String.StringCTOR(buffer, index, bufSize-index);
|
|
|
|
//markples: Pad if necessary, open up access to String if you
|
|
//don't want to make a new string here
|
|
if ((flags&LeftAlign)!=0) {
|
|
local = local.PadRight(buffLength,paddingChar);
|
|
} else {
|
|
local = local.PadLeft(buffLength,paddingChar);
|
|
}
|
|
|
|
return local;
|
|
}
|
|
|
|
//FCIMPL5(LPVOID, ParseNumbers::LongToString, INT32 radix, INT32 width,
|
|
// INT64 n, WCHAR paddingChar, INT32 flags)
|
|
public static String LongToString(long n, int radix, int width,
|
|
char paddingChar, int flags) {
|
|
// rusa: see also Lightning\Src\VM\COMUtilNative.cpp::LongToString,
|
|
bool isNegative = false;
|
|
int charVal;
|
|
ulong l;
|
|
int buffLength=0;
|
|
|
|
//Longest possible string length for an integer in
|
|
//binary notation with prefix
|
|
int bufSize = 67;
|
|
char[] buffer = new char[67];
|
|
int index=bufSize;
|
|
|
|
if (radix<MinRadix || radix>MaxRadix) {
|
|
throw new ArgumentException("Argument_InvalidBase");
|
|
//COMPlusThrowArgumentException(L"radix", L"Arg_InvalidBase");
|
|
}
|
|
|
|
//If the number is negative, make it positive and remember the sign.
|
|
if (n<0) {
|
|
isNegative=true;
|
|
// For base 10, write out -num, but other bases write out the
|
|
// 2's complement bit pattern
|
|
if (10==radix)
|
|
l = (ulong)(-n);
|
|
else
|
|
l = (ulong)n; // REVIEW: markples: comment suggests ~n wanted
|
|
} else {
|
|
l=(ulong)n;
|
|
}
|
|
|
|
if ((flags & PrintAsI1) != 0) {
|
|
l = l&0xFF;
|
|
} else if ((flags&PrintAsI2) != 0) {
|
|
l = l&0xFFFF;
|
|
} else if ((flags&PrintAsI4) != 0) {
|
|
l=l&0xFFFFFFFF;
|
|
}
|
|
|
|
//Special case the 0.
|
|
if (0==l) {
|
|
buffer[--index]='0';
|
|
} else {
|
|
//Pull apart the number and put the digits (in
|
|
//reverse order) into the buffer.
|
|
for ( ; l>0; l=l/(ulong)radix) {
|
|
if ((charVal=(int)(l%(ulong)radix))<10) {
|
|
buffer[--index] = (char)(charVal + '0');
|
|
} else {
|
|
buffer[--index] = (char)(charVal + 'a' - 10);
|
|
}
|
|
}
|
|
}
|
|
|
|
//If they want the base, append that to the string (in reverse order)
|
|
if (radix!=10 && ((flags&PrintBase)!=0)) {
|
|
if (16==radix) {
|
|
buffer[--index]='x';
|
|
buffer[--index]='0';
|
|
} else if (8==radix) {
|
|
buffer[--index]='0';
|
|
} else if ((flags&PrintRadixBase)!=0) {
|
|
buffer[--index]='#';
|
|
buffer[--index]=(char)((radix%10)+'0');
|
|
buffer[--index]=(char)((radix/10)+'0');
|
|
}
|
|
}
|
|
|
|
if (10==radix) {
|
|
if (isNegative) {
|
|
//If it was negative, append the sign.
|
|
buffer[--index]='-';
|
|
} else if ((flags&PrintSign)!=0) {
|
|
//else if they requested, add the '+';
|
|
buffer[--index]='+';
|
|
} else if ((flags&PrefixSpace)!=0) {
|
|
//If they requested a leading space, put it on.
|
|
buffer[--index]=' ';
|
|
}
|
|
}
|
|
|
|
//Figure out the size of our string.
|
|
if (width<=bufSize-index) {
|
|
buffLength=bufSize-index;
|
|
} else {
|
|
buffLength=width;
|
|
}
|
|
|
|
String local = String.StringCTOR(buffer, index, bufSize-index);
|
|
|
|
//markples: Pad if necessary, open up access to String if you
|
|
//don't want to make a new string here
|
|
if ((flags&LeftAlign)!=0) {
|
|
local = local.PadRight(buffLength,paddingChar);
|
|
} else {
|
|
local = local.PadLeft(buffLength,paddingChar);
|
|
}
|
|
|
|
return local;
|
|
}
|
|
|
|
private static String LongToString(int radix, int width, long l, char paddingChar, int flags) {
|
|
// Just in case the C# compiler gets smart enough to inline the
|
|
// original version of the above method (which calls the original
|
|
// version of this method to avoid a problem with fastcall not
|
|
// liking int64 as a first argument).
|
|
return LongToString(l, radix, width, paddingChar, flags);
|
|
}
|
|
}
|
|
}
|