[PATCH] NSScanner: Add string to long long hex conversion support

Abbas Raza <[email protected]>
Newsgroups gmane.comp.lib.gnustep.bugs
Message-ID <CABUCQqF74=qDC3fdGq2zWQGdx52j29_g-a7B+O7pfp-07P_PLw@mail.gmail.com>
Signed-off-by: Abbas Raza <[email protected]>
---
 Headers/Foundation/NSScanner.h |   3 +-
 Source/NSScanner.m             | 143 ++++++++++++++++++++++++++++++
+++++++++--
 Tests/base/NSScanner/test01.m  |  17 +++++
 3 files changed, 157 insertions(+), 6 deletions(-)

diff --git a/Headers/Foundation/NSScanner.h b/Headers/Foundation/NSScanner.h
index a3b61dd..b103642 100644
--- a/Headers/Foundation/NSScanner.h
+++ b/Headers/Foundation/NSScanner.h
@@ -94,6 +94,7 @@ extern "C" {
 - (BOOL) scanInt: (int*)value;
 - (BOOL) scanHexInt: (unsigned int*)value;
 - (BOOL) scanLongLong: (long long*)value;
+- (BOOL) scanHexLongLong: (unsigned long long*)value;
 - (BOOL) scanFloat: (float*)value;
 - (BOOL) scanDouble: (double*)value;
 - (BOOL) scanString: (NSString*)string intoString: (NSString**)value;
@@ -116,8 +117,6 @@ extern "C" {
 /** Not implemented */
 - (BOOL) scanHexFloat: (float *)result;
 /** Not implemented */
-- (BOOL) scanHexLongLong: (unsigned long long *)result;
-/** Not implemented */
 - (BOOL) scanInteger: (NSInteger *)value;
 #endif
 @end
diff --git a/Source/NSScanner.m b/Source/NSScanner.m
index 26c3077..149be49 100644
--- a/Source/NSScanner.m
+++ b/Source/NSScanner.m
@@ -629,6 +629,145 @@ typedef GSString  *ivars;
 #endif /* defined(LLONG_MAX) */
 }

+/*
+ * Scan an unsigned long long of the given radix into value.
+ * Internal version used by scanHexLongLong: .
+ */
+- (BOOL) scanUnsignedLongLong_: (unsigned long long int*)value
+                   radix: (NSUInteger)radix
+               gotDigits: (BOOL)gotDigits
+{
+#if defined(ULLONG_MAX)
+  unsigned long long int       num = 0;
+  unsigned long long int       numLimit, digitLimit, digitValue;
+  BOOL         overflow = NO;
+  unsigned int saveScanLocation = _scanLocation;
+
+  /* Set limits */
+  numLimit = ULLONG_MAX / radix;
+  digitLimit = ULLONG_MAX % radix;
+
+  /* Process digits */
+  while (_scanLocation < myLength())
+    {
+      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;
+       }
+      if (digitValue >= radix)
+       break;
+      if (!overflow)
+       {
+         if ((num > numLimit)
+           || ((num == numLimit) && (digitValue > digitLimit)))
+           overflow = YES;
+         else
+           num = num * radix + digitValue;
+       }
+      _scanLocation++;
+      gotDigits = YES;
+    }
+
+  /* Save result */
+  if (!gotDigits)
+    {
+      _scanLocation = saveScanLocation;
+      return NO;
+    }
+  if (value)
+    {
+      if (overflow)
+       *value = ULLONG_MAX;
+      else
+       *value = num;
+    }
+  return YES;
+#else /* defined(ULLONG_MAX) */
+  /*
+   * Provide compile-time warning and run-time exception.
+   */
+#    warning "Can't use unsigned long long variables."
+  [NSException raise: NSGenericException
+              format: @"Can't use unsigned long long variables."];
+  return NO;
+#endif /* defined(ULLONG_MAX) */
+}
+
+/**
+ * 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 gotDigits: NO])
+    return YES;
+  _scanLocation = saveScanLocation;
+  return NO;
+}
+
 /**
  * Not implemented.
  */
@@ -1140,10 +1279,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
diff --git a/Tests/base/NSScanner/test01.m b/Tests/base/NSScanner/test01.m
index 63dd912..13cc29e 100644
--- a/Tests/base/NSScanner/test01.m
+++ b/Tests/base/NSScanner/test01.m
@@ -51,6 +51,19 @@ static BOOL scanHex(NSString *str,
   return ((expectValue == 1) ? (expectedValue == *retp) : YES
           && expectedScanLoc == [scn scanLocation]);
 }
+
+static BOOL scanHexLongLong(NSString *str,
+                   int expectValue,
+                   unsigned long long expectedValue,
+                   int expectedScanLoc,
+                   int *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)
@@ -104,6 +117,10 @@ int main()
   PASS(scanHex(@"1234FOO", 1, 0x1234F, 5, &ret)
        && scanHex(@"01234FOO", 1, 0x1234F, 6, &ret),
        "NSScanner hex (non-digits terminate scan)");
+
+  PASS(scanHexLongLong(@"1234FOO", 1, 0x1234F, 5, &ret)
+       && scanHexLongLong(@"01234FOO", 1, 0x1234F, 6, &ret),
+       "NSScanner hex long long (non-digits terminate scan)");
  /* dbl1 = 123.456;
   dbl2 = 123.45678901234567890123456789012345678901234567;
   dbl3 = 1.23456789;
--
2.1.4

_______________________________________________
Bug-gnustep mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/bug-gnustep
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.