scanHexLongLong inspider by Abbas Raza

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@40070 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
rfm 2016-09-06 07:57:17 +00:00
parent 40f50d9f2b
commit 0c1d87020d
4 changed files with 310 additions and 128 deletions

View file

@ -1,3 +1,12 @@
2016-09-06 Richard Frith-Macdonald <rfm@gnu.org>
* Headers/Foundation/NSScanner.h:
* Source/NSScanner.m:
* Tests/base/NSScanner/test01.m:
Add support for scanHexLongLong method inspired/based on patch from
Abbas Raza, but with minor bugfixes, reduced code duplication, and
avoiding compiler warnings.
2016-08-30 Abbas Raza <abbas.raza.1707@gmail.com>
* Source/NSJSONSerialization.m: Generate NSError rather than raising

View file

@ -106,18 +106,19 @@ extern "C" {
#if OS_API_VERSION(GS_API_NONE, GS_API_NONE)
- (BOOL) scanRadixUnsignedInt: (unsigned int*)value;
- (BOOL) scanRadixUnsignedLongLong: (unsigned long long*)value;
#endif
#if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST)
- (BOOL) scanDecimal: (NSDecimal*)value;
#endif
#if OS_API_VERSION(MAC_OS_X_VERSION_10_5, GS_API_LATEST)
- (BOOL) scanHexLongLong: (unsigned long long*)value;
/** Not implemented */
- (BOOL) scanHexDouble: (double *)result;
/** Not implemented */
- (BOOL) scanHexFloat: (float *)result;
/** Not implemented */
- (BOOL) scanHexLongLong: (unsigned long long *)result;
/** Not implemented */
- (BOOL) scanInteger: (NSInteger *)value;
#endif
@end

View file

@ -42,6 +42,9 @@
# error Neither LLONG_MAX nor __LONG_LONG_MAX__ found
# endif
#endif
#if !defined(ULLONG_MAX)
# define ULLONG_MAX (LLONG_MAX * 2ULL + 1)
#endif
#include <math.h>
#include <ctype.h> /* FIXME: May go away once I figure out Unicode */
@ -352,22 +355,20 @@ typedef GSString *ivars;
return NO;
}
/*
* Scan an unsigned int of the given radix into value.
* Internal version used by scanRadixUnsignedInt: and scanHexInt: .
/* Scan an unsigned long long of the given radix into value.
* Internal version used by scanHexInt:, scanHexLongLong: etc.
*/
- (BOOL) scanUnsignedInt_: (unsigned int*)value
radix: (NSUInteger)radix
gotDigits: (BOOL)gotDigits
- (BOOL) scanUnsignedLongLong_: (unsigned long long int*)value
radix: (NSUInteger)radix
maximum: (unsigned long long)max
gotDigits: (BOOL)gotDigits
{
unsigned int num = 0;
unsigned int numLimit, digitLimit, digitValue;
BOOL overflow = NO;
unsigned int saveScanLocation = _scanLocation;
/* Set limits */
numLimit = UINT_MAX / radix;
digitLimit = UINT_MAX % radix;
unsigned long long int num = 0;
unsigned long long int numLimit = max / radix;
unsigned long long int digitLimit = max % radix;
unsigned long long int digitValue = 0;
BOOL overflow = NO;
unsigned int saveScanLocation = _scanLocation;
/* Process digits */
while (_scanLocation < myLength())
@ -375,43 +376,49 @@ typedef GSString *ivars;
unichar digit = myCharacter(_scanLocation);
switch (digit)
{
case '0': digitValue = 0; break;
case '1': digitValue = 1; break;
case '2': digitValue = 2; break;
case '3': digitValue = 3; break;
case '4': digitValue = 4; break;
case '5': digitValue = 5; break;
case '6': digitValue = 6; break;
case '7': digitValue = 7; break;
case '8': digitValue = 8; break;
case '9': digitValue = 9; break;
case 'a': digitValue = 0xA; break;
case 'b': digitValue = 0xB; break;
case 'c': digitValue = 0xC; break;
case 'd': digitValue = 0xD; break;
case 'e': digitValue = 0xE; break;
case 'f': digitValue = 0xF; break;
case 'A': digitValue = 0xA; break;
case 'B': digitValue = 0xB; break;
case 'C': digitValue = 0xC; break;
case 'D': digitValue = 0xD; break;
case 'E': digitValue = 0xE; break;
case 'F': digitValue = 0xF; break;
default:
digitValue = radix;
break;
}
{
case '0': digitValue = 0; break;
case '1': digitValue = 1; break;
case '2': digitValue = 2; break;
case '3': digitValue = 3; break;
case '4': digitValue = 4; break;
case '5': digitValue = 5; break;
case '6': digitValue = 6; break;
case '7': digitValue = 7; break;
case '8': digitValue = 8; break;
case '9': digitValue = 9; break;
case 'a': digitValue = 0xA; break;
case 'b': digitValue = 0xB; break;
case 'c': digitValue = 0xC; break;
case 'd': digitValue = 0xD; break;
case 'e': digitValue = 0xE; break;
case 'f': digitValue = 0xF; break;
case 'A': digitValue = 0xA; break;
case 'B': digitValue = 0xB; break;
case 'C': digitValue = 0xC; break;
case 'D': digitValue = 0xD; break;
case 'E': digitValue = 0xE; break;
case 'F': digitValue = 0xF; break;
default:
digitValue = radix;
break;
}
if (digitValue >= radix)
break;
{
break;
}
if (!overflow)
{
if ((num > numLimit)
|| ((num == numLimit) && (digitValue > digitLimit)))
overflow = YES;
else
num = num * radix + digitValue;
}
{
if ((num > numLimit)
|| ((num == numLimit) && (digitValue > digitLimit)))
{
overflow = YES;
}
else
{
num = num * radix + digitValue;
}
}
_scanLocation++;
gotDigits = YES;
}
@ -425,31 +432,36 @@ typedef GSString *ivars;
if (value)
{
if (overflow)
*value = UINT_MAX;
{
*value = ULLONG_MAX;
}
else
*value = num;
{
*value = num;
}
}
return YES;
}
/**
* After initial skipping (if any), this method scans an unsigned
* integer value placing it in <em>intValue</em> if that is not null.
* integer placing it in <em>value</em> if that is not null.
* If the number begins with "0x" or "0X" it is treated as hexadecimal,
* otherwise if the number begins with "0" it is treated as octal,
* otherwise the number is treated as decimal.
* <br/>
* Returns YES if anything is scanned, NO otherwise.
* <br/>
* On overflow, INT_MAX or INT_MIN is put into <em>intValue</em>
* On overflow, UINT_MAX is put into <em>value</em>
* <br/>
* Scans past any excess digits
*/
- (BOOL) scanRadixUnsignedInt: (unsigned int*)value
{
unsigned int radix;
BOOL gotDigits = NO;
unsigned int saveScanLocation = _scanLocation;
unsigned int radix;
unsigned long long tmp;
BOOL gotDigits = NO;
unsigned int saveScanLocation = _scanLocation;
/* Skip whitespace */
if (!skipToNextField())
@ -478,8 +490,78 @@ typedef GSString *ivars;
}
}
}
if ([self scanUnsignedInt_: value radix: radix gotDigits: gotDigits])
return YES;
if ([self scanUnsignedLongLong_: &tmp
radix: radix
maximum: UINT_MAX
gotDigits: gotDigits])
{
if (tmp > UINT_MAX)
{
*value = UINT_MAX;
}
else
{
*value = (unsigned int)tmp;
}
return YES;
}
_scanLocation = saveScanLocation;
return NO;
}
/**
* After initial skipping (if any), this method scans an unsigned
* long long integer placing it in <em>value</em> if that is not null.
* If the number begins with "0x" or "0X" it is treated as hexadecimal,
* otherwise if the number begins with "0" it is treated as octal,
* otherwise the number is treated as decimal.
* <br/>
* Returns YES if anything is scanned, NO otherwise.
* <br/>
* On overflow, ULLONG_MAX is put into <em>value</em>
* <br/>
* Scans past any excess digits
*/
- (BOOL) scanRadixUnsignedLongLong: (unsigned long long*)value
{
unsigned int radix;
BOOL gotDigits = NO;
unsigned int saveScanLocation = _scanLocation;
/* Skip whitespace */
if (!skipToNextField())
{
_scanLocation = saveScanLocation;
return NO;
}
/* Check radix */
radix = 10;
if ((_scanLocation < myLength()) && (myCharacter(_scanLocation) == '0'))
{
radix = 8;
_scanLocation++;
gotDigits = YES;
if (_scanLocation < myLength())
{
switch (myCharacter(_scanLocation))
{
case 'x':
case 'X':
_scanLocation++;
radix = 16;
gotDigits = NO;
break;
}
}
}
if ([self scanUnsignedLongLong_: value
radix: radix
maximum: ULLONG_MAX
gotDigits: gotDigits])
{
return YES;
}
_scanLocation = saveScanLocation;
return NO;
}
@ -497,7 +579,8 @@ typedef GSString *ivars;
*/
- (BOOL) scanHexInt: (unsigned int*)value
{
unsigned int saveScanLocation = _scanLocation;
unsigned int saveScanLocation = _scanLocation;
unsigned long long tmp;
/* Skip whitespace */
if (!skipToNextField())
@ -527,8 +610,14 @@ typedef GSString *ivars;
_scanLocation--; // Just scan the zero.
}
}
if ([self scanUnsignedInt_: value radix: 16 gotDigits: NO])
return YES;
if ([self scanUnsignedLongLong_: &tmp
radix: 16
maximum: UINT_MAX
gotDigits: NO])
{
*value = (unsigned int)tmp;
return YES;
}
_scanLocation = saveScanLocation;
return NO;
}
@ -547,7 +636,6 @@ typedef GSString *ivars;
*/
- (BOOL) scanLongLong: (long long *)value
{
#if defined(LLONG_MAX)
unsigned long long num = 0;
const unsigned long long limit = ULLONG_MAX / 10;
BOOL negative = NO;
@ -618,15 +706,60 @@ typedef GSString *ivars;
}
}
return YES;
#else /* defined(LLONG_MAX) */
/*
* Provide compile-time warning and run-time exception.
*/
# warning "Can't use long long variables."
[NSException raise: NSGenericException
format: @"Can't use long long variables."];
}
/**
* After initial skipping (if any), this method scans a hexadecimal
* long long value (optionally prefixed by "0x" or "0X"),
* placing it in <em>longLongValue</em> if that is not null.
* <br/>
* Returns YES if anything is scanned, NO otherwise.
* <br/>
* On overflow, ULLONG_MAX or ULLONG_MAX is put into <em>longLongValue</em>
* <br/>
* Scans past any excess digits
*/
- (BOOL) scanHexLongLong: (unsigned long long*)value
{
unsigned int saveScanLocation = _scanLocation;
/* Skip whitespace */
if (!skipToNextField())
{
_scanLocation = saveScanLocation;
return NO;
}
if ((_scanLocation < myLength()) && (myCharacter(_scanLocation) == '0'))
{
_scanLocation++;
if (_scanLocation < myLength())
{
switch (myCharacter(_scanLocation))
{
case 'x':
case 'X':
_scanLocation++; // Scan beyond the 0x prefix
break;
default:
_scanLocation--; // Scan from the initial digit
break;
}
}
else
{
_scanLocation--; // Just scan the zero.
}
}
if ([self scanUnsignedLongLong_: value
radix: 16
maximum: ULLONG_MAX
gotDigits: NO])
{
return YES;
}
_scanLocation = saveScanLocation;
return NO;
#endif /* defined(LLONG_MAX) */
}
/**
@ -1140,10 +1273,6 @@ typedef GSString *ivars;
{
return NO; // FIXME
}
- (BOOL) scanHexLongLong: (unsigned long long *)result
{
return NO; // FIXME
}
- (BOOL) scanInteger: (NSInteger *)value
{
#if GS_SIZEOF_VOIDP == GS_SIZEOF_INT

View file

@ -8,102 +8,145 @@
static BOOL scanInt(int value, int *retp)
{
NSString *str = [[NSNumber numberWithInt:value] description];
NSScanner *scn = [NSScanner scannerWithString:str];
return ([scn scanInt:retp] && value == *retp);
NSString *str = [[NSNumber numberWithInt: value] description];
NSScanner *scn = [NSScanner scannerWithString: str];
return ([scn scanInt: retp] && value == *retp);
}
/* this didn't seem to be used in any of the NSScanner guile tests
so this function is untested.
*/
static BOOL scanIntOverflow(int value, int *retp)
{
NSString *str = [[NSNumber numberWithFloat:value] description];
NSScanner *scn = [NSScanner scannerWithString:str];
NSString *str = [[NSNumber numberWithFloat: value] description];
NSScanner *scn = [NSScanner scannerWithString: str];
int intmax = 2147483647;
int intmin = (0 - (intmax - 1));
return ([scn scanInt:retp]
&& (0 > value) ? (intmin == *retp) : (intmax == *retp));
return ([scn scanInt: retp]
&& (0 > value) ? (intmin == *retp) : (intmax == *retp));
}
*/
#if defined(GNUSTEP_BASE_LIBRARY)
static BOOL scanRadixUnsigned(NSString *str,
int expectValue,
unsigned int expectedValue,
int expectedScanLoc,
int *retp)
int expectValue,
unsigned int expectedValue,
int expectedScanLoc,
unsigned *retp)
{
NSScanner *scn = [NSScanner scannerWithString:str];
[scn scanRadixUnsignedInt:retp];
NSScanner *scn = [NSScanner scannerWithString: str];
[scn scanRadixUnsignedInt: retp];
return ((expectValue == 1) ? (expectedValue == *retp) : YES
&& expectedScanLoc == [scn scanLocation]);
&& expectedScanLoc == [scn scanLocation]);
}
static BOOL scanRadixUnsignedLongLong(NSString *str,
int expectValue,
unsigned int expectedValue,
int expectedScanLoc,
unsigned long long *retp)
{
NSScanner *scn = [NSScanner scannerWithString: str];
[scn scanRadixUnsignedLongLong: retp];
return ((expectValue == 1) ? (expectedValue == *retp) : YES
&& expectedScanLoc == [scn scanLocation]);
}
#endif
static BOOL scanHex(NSString *str,
int expectValue,
unsigned int expectedValue,
int expectedScanLoc,
int *retp)
int expectValue,
unsigned int expectedValue,
int expectedScanLoc,
unsigned *retp)
{
NSScanner *scn = [NSScanner scannerWithString:str];
[scn scanHexInt:retp];
NSScanner *scn = [NSScanner scannerWithString: str];
[scn scanHexInt: retp];
return ((expectValue == 1) ? (expectedValue == *retp) : YES
&& expectedScanLoc == [scn scanLocation]);
&& expectedScanLoc == [scn scanLocation]);
}
static BOOL scanDouble(NSString *str,
double expectedValue,
double *retp)
static BOOL scanHexLongLong(NSString *str,
int expectValue,
unsigned long long expectedValue,
int expectedScanLoc,
unsigned long long *retp)
{
NSScanner *scn = [NSScanner scannerWithString:str];
[scn scanDouble:retp];
NSScanner *scn = [NSScanner scannerWithString: str];
[scn scanHexLongLong: retp];
return ((expectValue == 1) ? (expectedValue == *retp) : YES
&& expectedScanLoc == [scn scanLocation]);
}
static BOOL scanDouble(NSString *str,
double expectedValue,
double *retp)
{
NSScanner *scn = [NSScanner scannerWithString: str];
[scn scanDouble: retp];
return ((0.00000000001 >= fabs(expectedValue - *retp))
&& [str length] == [scn scanLocation]);
&& [str length] == [scn scanLocation]);
}
int main()
{
NSAutoreleasePool *arp = [NSAutoreleasePool new];
int ret;
double dret;
int intmax = 2147483647;
int intmin = (0 - (intmax - 1));
NSScanner *scn;
float flt;
NSString *em;
NSAutoreleasePool *arp = [NSAutoreleasePool new];
long long lret;
int ret;
double dret;
int intmax = 2147483647;
int intmin = (0 - (intmax - 1));
NSScanner *scn;
float flt;
NSString *em;
PASS(scanInt((intmax - 20),&ret), "NSScanner large ints");
PASS(scanInt((intmin + 20),&ret), "NSScanner small ints");
scn = [NSScanner scannerWithString:@"1234F00"];
PASS(([scn scanInt:&ret] && ([scn scanLocation] == 4)),
"NSScanner non-digits terminate scan");
"NSScanner non-digits terminate scan");
scn = [NSScanner scannerWithString:@"junk"];
PASS((![scn scanInt:&ret] && ([scn scanLocation] == 0)),
"NSScanner non-digits terminate scan");
"NSScanner non-digits terminate scan");
scn = [NSScanner scannerWithString:@"junk"];
PASS(![scn scanInt:&ret] && ([scn scanLocation] == 0),
"NSScanner non-digits dont consume characters to be skipped");
"NSScanner non-digits dont consume characters to be skipped");
#if defined(GNUSTEP_BASE_LIBRARY)
PASS(scanRadixUnsigned(@"1234FOO", 1, 1234, 4, &ret)
&& scanRadixUnsigned(@"01234FOO", 1, 01234, 5, &ret)
&& scanRadixUnsigned(@"0x1234FOO", 1, 0x1234F, 7, &ret)
&& scanRadixUnsigned(@"0X1234FOO", 1, 0x1234F, 7, &ret)
&& scanRadixUnsigned(@"012348FOO", 1, 01234, 5, &ret),
"NSScanner radiux unsigned (non-digits terminate scan)");
PASS(scanRadixUnsigned(@"1234FOO", 1, 1234, 4, (unsigned*)&ret)
&& scanRadixUnsigned(@"01234FOO", 1, 01234, 5, (unsigned*)&ret)
&& scanRadixUnsigned(@"0x1234FOO", 1, 0x1234F, 7, (unsigned*)&ret)
&& scanRadixUnsigned(@"0X1234FOO", 1, 0x1234F, 7, (unsigned*)&ret)
&& scanRadixUnsigned(@"012348FOO", 1, 01234, 5, (unsigned*)&ret),
"NSScanner radiux unsigned (non-digits terminate scan)");
PASS(scanRadixUnsigned(@"FOO", 0, 0, 0, &ret)
&& scanRadixUnsigned(@" FOO", 0, 0, 0, &ret)
&& scanRadixUnsigned(@" 0x ", 0, 0, 0, &ret),
"NSScanner radiux unsigned (non-digits dont move scan)");
PASS(scanRadixUnsignedLongLong(
@"1234FOO", 1, 1234, 4, (unsigned long long*)&lret)
&& scanRadixUnsignedLongLong(
@"01234FOO", 1, 01234, 5, (unsigned long long*)&lret)
&& scanRadixUnsignedLongLong(
@"0x1234FOO", 1, 0x1234F, 7, (unsigned long long*)&lret)
&& scanRadixUnsignedLongLong(
@"0X1234FOO", 1, 0x1234F, 7, (unsigned long long*)&lret)
&& scanRadixUnsignedLongLong(
@"012348FOO", 1, 01234, 5, (unsigned long long*)&lret),
"NSScanner radiux unsigned (non-digits terminate scan)");
PASS(scanRadixUnsigned(@"FOO", 0, 0, 0, (unsigned*)&ret)
&& scanRadixUnsigned(@" FOO", 0, 0, 0, (unsigned*)&ret)
&& scanRadixUnsigned(@" 0x ", 0, 0, 0, (unsigned*)&ret),
"NSScanner radiux unsigned (non-digits dont move scan)");
#endif
PASS(scanHex(@"1234FOO", 1, 0x1234F, 5, &ret)
&& scanHex(@"01234FOO", 1, 0x1234F, 6, &ret),
"NSScanner hex (non-digits terminate scan)");
PASS(scanHex(@"1234FOO", 1, 0x1234F, 5, (unsigned*)&ret)
&& scanHex(@"01234FOO", 1, 0x1234F, 6, (unsigned*)&ret),
"NSScanner hex (non-digits terminate scan)");
PASS(scanHexLongLong(@"1234FOO", 1, 0x1234F, 5, (unsigned long long*)&lret)
&& scanHexLongLong(@"01234FOO", 1, 0x1234F, 6, (unsigned long long*)&lret),
"NSScanner hex long long (non-digits terminate scan)");
/* dbl1 = 123.456;
dbl2 = 123.45678901234567890123456789012345678901234567;
dbl3 = 1.23456789;