aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Foundation/GTMNSScanner+Unsigned.h30
-rw-r--r--Foundation/GTMNSScanner+Unsigned.m60
-rw-r--r--Foundation/GTMNSScanner+UnsignedTest.m116
-rw-r--r--GTM.xcodeproj/project.pbxproj12
-rw-r--r--GTMiPhone.xcodeproj/project.pbxproj10
5 files changed, 228 insertions, 0 deletions
diff --git a/Foundation/GTMNSScanner+Unsigned.h b/Foundation/GTMNSScanner+Unsigned.h
new file mode 100644
index 0000000..307ac82
--- /dev/null
+++ b/Foundation/GTMNSScanner+Unsigned.h
@@ -0,0 +1,30 @@
+//
+// GTMNSScanner+Unsigned.h
+//
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Foundation/Foundation.h>
+
+// Adds support for working with unsigned values. Unsigned values are digits
+// only [0-9]. There is no support for "+" or "-" signs. Overflow is handled
+// by returning "YES" and the maximum value allowed for the type.
+@interface NSScanner (GTMUnsignedAdditions)
+
+- (BOOL)gtm_scanUnsignedInt:(unsigned int *)value;
+- (BOOL)gtm_scanUInteger:(NSUInteger *)value;
+- (BOOL)gtm_scanUnsignedLongLong:(unsigned long long *)value;
+
+@end
diff --git a/Foundation/GTMNSScanner+Unsigned.m b/Foundation/GTMNSScanner+Unsigned.m
new file mode 100644
index 0000000..9228ba2
--- /dev/null
+++ b/Foundation/GTMNSScanner+Unsigned.m
@@ -0,0 +1,60 @@
+//
+// GTMNSScanner+Unsigned.m
+//
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMNSScanner+Unsigned.h"
+#include <stdlib.h>
+#include <limits.h>
+
+@implementation NSScanner (GTMUnsignedAdditions)
+
+- (BOOL)gtm_scanUnsignedInt:(unsigned int *)value {
+ unsigned long long uLongLongValue = 0;
+ BOOL wasGood = [self gtm_scanUnsignedLongLong:&uLongLongValue];
+ if (wasGood && value) {
+ if (uLongLongValue > UINT_MAX) {
+ *value = UINT_MAX;
+ } else {
+ *value = (unsigned int)uLongLongValue;
+ }
+ }
+ return wasGood;
+}
+
+- (BOOL)gtm_scanUInteger:(NSUInteger *)value {
+#if defined(__LP64__) && __LP64__
+ return [self gtm_scanUnsignedLongLong:(unsigned long long*)value];
+#else
+ return [self gtm_scanUnsignedInt:value];
+#endif // defined(__LP64__) && __LP64__
+}
+
+- (BOOL)gtm_scanUnsignedLongLong:(unsigned long long *)value {
+ // Slow path
+ NSCharacterSet *decimalSet = [NSCharacterSet decimalDigitCharacterSet];
+ NSString *digitString = nil;
+ BOOL wasGood = [self scanCharactersFromSet:decimalSet intoString:&digitString];
+ if (wasGood) {
+ const char *digitChars = [digitString UTF8String];
+ if (value) {
+ *value = strtoull(digitChars, NULL, 10);
+ }
+ }
+ return wasGood;
+}
+
+@end
diff --git a/Foundation/GTMNSScanner+UnsignedTest.m b/Foundation/GTMNSScanner+UnsignedTest.m
new file mode 100644
index 0000000..aab92f3
--- /dev/null
+++ b/Foundation/GTMNSScanner+UnsignedTest.m
@@ -0,0 +1,116 @@
+//
+// GTMNSScanner+UnsignedTest.m
+//
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMSenTestCase.h"
+#import "GTMNSScanner+Unsigned.h"
+
+@interface GTMNSScanner_UnsignedTest : GTMTestCase
+@end
+
+@implementation GTMNSScanner_UnsignedTest
+
+#define TEST_BLOCK(A_MAX_VALUE) \
+ { @"-1", 0, NO, 0 }, \
+ { @"- 1", 0, NO, 0 }, \
+ { @" - 1", 0, NO, 0 }, \
+ { @"+ 1", 1, NO, 0 }, \
+ { @" + 1", 1, NO, 0 }, \
+ { @"0", 0, YES, 1 }, \
+ { @"a", 0, NO, 0 }, \
+ { @" ", 0, NO, 0 }, \
+ { @"-1a", 0, NO, 0 }, \
+ { @"a1", 0, NO, 0 }, \
+ { @"1 ", 1, YES, 1 }, \
+ { @"2 1 ", 2, YES, 1 }, \
+ { @" 2 1 ", 2, YES, 2 }, \
+ { @"99999999999999999999999999999999999", A_MAX_VALUE, YES, 35 }
+
+- (void)testScanUnsignedInt {
+ struct {
+ NSString *string;
+ unsigned int val;
+ BOOL goodScan;
+ NSUInteger location;
+ } testStruct[] = {
+ TEST_BLOCK(UINT_MAX),
+ };
+ for (size_t i = 0; i < sizeof(testStruct) / sizeof(testStruct[0]); ++i) {
+ NSScanner *scanner = [NSScanner scannerWithString:testStruct[i].string];
+ STAssertNotNil(scanner, nil);
+ unsigned int value;
+ BOOL isGood = [scanner gtm_scanUnsignedInt:&value];
+ STAssertEquals((int)isGood, (int)testStruct[i].goodScan,
+ @"%@", testStruct[i].string);
+ if (isGood && testStruct[i].goodScan) {
+ STAssertEquals(value, testStruct[i].val, @"%@", testStruct[i].string);
+ }
+ STAssertEquals(testStruct[i].location, [scanner scanLocation],
+ @"%@", testStruct[i].string);
+ }
+}
+
+- (void)testScanUInteger {
+ struct {
+ NSString *string;
+ NSUInteger val;
+ BOOL goodScan;
+ NSUInteger location;
+ } testStruct[] = {
+ TEST_BLOCK(NSUIntegerMax),
+ };
+ for (size_t i = 0; i < sizeof(testStruct) / sizeof(testStruct[0]); ++i) {
+ NSScanner *scanner = [NSScanner scannerWithString:testStruct[i].string];
+ STAssertNotNil(scanner, nil);
+ NSUInteger value;
+ BOOL isGood = [scanner gtm_scanUInteger:&value];
+ STAssertEquals((int)isGood, (int)testStruct[i].goodScan,
+ @"%@", testStruct[i].string);
+ if (isGood && testStruct[i].goodScan) {
+ STAssertEquals(value, testStruct[i].val, @"%@", testStruct[i].string);
+ }
+ STAssertEquals(testStruct[i].location, [scanner scanLocation],
+ @"%@", testStruct[i].string);
+ }
+}
+
+- (void)testScanUnsignedLongLong {
+ struct {
+ NSString *string;
+ unsigned long long val;
+ BOOL goodScan;
+ NSUInteger location;
+ } testStruct[] = {
+ TEST_BLOCK(ULLONG_MAX),
+ { @"4294967296", ((unsigned long long)UINT_MAX) + 1, YES, 10 }
+ };
+ for (size_t i = 0; i < sizeof(testStruct) / sizeof(testStruct[0]); ++i) {
+ NSScanner *scanner = [NSScanner scannerWithString:testStruct[i].string];
+ STAssertNotNil(scanner, nil);
+ unsigned long long value;
+ BOOL isGood = [scanner gtm_scanUnsignedLongLong:&value];
+ STAssertEquals((int)isGood, (int)testStruct[i].goodScan,
+ @"%@", testStruct[i].string);
+ if (isGood && testStruct[i].goodScan) {
+ STAssertEquals(value, testStruct[i].val, @"%@", testStruct[i].string);
+ }
+ STAssertEquals(testStruct[i].location, [scanner scanLocation],
+ @"%@", testStruct[i].string);
+ }
+}
+
+@end
diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj
index 51c6121..4d814d2 100644
--- a/GTM.xcodeproj/project.pbxproj
+++ b/GTM.xcodeproj/project.pbxproj
@@ -250,6 +250,9 @@
8BC045C20DAE899100C2D1CA /* GTMGeometryUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2800D198D0E009257D2 /* GTMGeometryUtilsTest.m */; };
8BC046B90DAE8C4B00C2D1CA /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */; };
8BC04CD80DB003D800C2D1CA /* GTMMethodCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8BCB59F011C00ED6009B6C40 /* GTMNSScanner+Unsigned.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BCB59EE11C00ED6009B6C40 /* GTMNSScanner+Unsigned.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8BCB59F111C00ED6009B6C40 /* GTMNSScanner+Unsigned.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BCB59EF11C00ED6009B6C40 /* GTMNSScanner+Unsigned.m */; };
+ 8BCB59F311C00EF6009B6C40 /* GTMNSScanner+UnsignedTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BCB59F211C00EF6009B6C40 /* GTMNSScanner+UnsignedTest.m */; };
8BD35B910FB22980009058F5 /* GTMNSScanner+JSON.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BD35B8E0FB22980009058F5 /* GTMNSScanner+JSON.h */; settings = {ATTRIBUTES = (Public, ); }; };
8BD35B920FB22980009058F5 /* GTMNSScanner+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD35B8F0FB22980009058F5 /* GTMNSScanner+JSON.m */; };
8BD35B940FB22986009058F5 /* GTMNSScanner+JSONTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD35B900FB22980009058F5 /* GTMNSScanner+JSONTest.m */; };
@@ -734,6 +737,9 @@
8BB77A0311B5A09900AB31AF /* GTMGoogleSearchTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMGoogleSearchTest.m; sourceTree = "<group>"; };
8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = "<absolute>"; };
8BC04D140DB0061300C2D1CA /* RunMacOSUnitTests.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = RunMacOSUnitTests.sh; sourceTree = "<group>"; };
+ 8BCB59EE11C00ED6009B6C40 /* GTMNSScanner+Unsigned.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSScanner+Unsigned.h"; sourceTree = "<group>"; };
+ 8BCB59EF11C00ED6009B6C40 /* GTMNSScanner+Unsigned.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSScanner+Unsigned.m"; sourceTree = "<group>"; };
+ 8BCB59F211C00EF6009B6C40 /* GTMNSScanner+UnsignedTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSScanner+UnsignedTest.m"; sourceTree = "<group>"; };
8BD35B8E0FB22980009058F5 /* GTMNSScanner+JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSScanner+JSON.h"; sourceTree = "<group>"; };
8BD35B8F0FB22980009058F5 /* GTMNSScanner+JSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSScanner+JSON.m"; sourceTree = "<group>"; };
8BD35B900FB22980009058F5 /* GTMNSScanner+JSONTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSScanner+JSONTest.m"; sourceTree = "<group>"; };
@@ -1442,6 +1448,9 @@
8BD35B8E0FB22980009058F5 /* GTMNSScanner+JSON.h */,
8BD35B8F0FB22980009058F5 /* GTMNSScanner+JSON.m */,
8BD35B900FB22980009058F5 /* GTMNSScanner+JSONTest.m */,
+ 8BCB59EE11C00ED6009B6C40 /* GTMNSScanner+Unsigned.h */,
+ 8BCB59EF11C00ED6009B6C40 /* GTMNSScanner+Unsigned.m */,
+ 8BCB59F211C00EF6009B6C40 /* GTMNSScanner+UnsignedTest.m */,
F42597760E23FE3A003BEA3E /* GTMNSString+FindFolder.h */,
F42597770E23FE3A003BEA3E /* GTMNSString+FindFolder.m */,
F42597780E23FE3A003BEA3E /* GTMNSString+FindFolderTest.m */,
@@ -1654,6 +1663,7 @@
8BDB8A991152E1B200C411B1 /* GTMNSAnimatablePropertyContainer.h in Headers */,
8B21DE56117E5CB7000E004F /* GTMLocalizedString.h in Headers */,
8BB7802E11B6C4EA00AB31AF /* GTMGoogleSearch.h in Headers */,
+ 8BCB59F011C00ED6009B6C40 /* GTMNSScanner+Unsigned.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2197,6 +2207,7 @@
0B1B9B8A10FECDA00084EE4B /* GTMStringEncodingTest.m in Sources */,
8B17FD15117638D500E7A908 /* GTMFoundationUnitTestingUtilities.m in Sources */,
8B455F5E1193870A00ABD707 /* GTMLocalizedStringTest.m in Sources */,
+ 8BCB59F311C00EF6009B6C40 /* GTMNSScanner+UnsignedTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2273,6 +2284,7 @@
0B1B9B8810FECD870084EE4B /* GTMStringEncoding.m in Sources */,
8BDB8A9A1152E1B200C411B1 /* GTMNSAnimatablePropertyContainer.m in Sources */,
8BB77A0611B5A0A100AB31AF /* GTMGoogleSearch.m in Sources */,
+ 8BCB59F111C00ED6009B6C40 /* GTMNSScanner+Unsigned.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj
index b8af71c..f9b508c 100644
--- a/GTMiPhone.xcodeproj/project.pbxproj
+++ b/GTMiPhone.xcodeproj/project.pbxproj
@@ -99,6 +99,8 @@
8BC04A750DAF145200C2D1CA /* GTMSystemVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */; };
8BC04D480DB0088500C2D1CA /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC04D470DB0088500C2D1CA /* libz.dylib */; };
8BC04DE80DB023D400C2D1CA /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */; };
+ 8BCB5AB111C02D7D009B6C40 /* GTMNSScanner+Unsigned.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BCB5AAF11C02D7D009B6C40 /* GTMNSScanner+Unsigned.m */; };
+ 8BCB5AB211C02D7D009B6C40 /* GTMNSScanner+UnsignedTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BCB5AB011C02D7D009B6C40 /* GTMNSScanner+UnsignedTest.m */; };
8BD35C920FB234E1009058F5 /* GTMNSScanner+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD35C900FB234E1009058F5 /* GTMNSScanner+JSON.m */; };
8BD35C930FB234E1009058F5 /* GTMNSScanner+JSONTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD35C910FB234E1009058F5 /* GTMNSScanner+JSONTest.m */; };
8BDA25130E759A6400C9769D /* GTMHTTPServerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA9210E033624007E31B5 /* GTMHTTPServerTest.m */; };
@@ -263,6 +265,9 @@
8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersion.m; sourceTree = "<group>"; };
8BC04D470DB0088500C2D1CA /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; };
+ 8BCB5AAE11C02D7D009B6C40 /* GTMNSScanner+Unsigned.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSScanner+Unsigned.h"; sourceTree = "<group>"; };
+ 8BCB5AAF11C02D7D009B6C40 /* GTMNSScanner+Unsigned.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSScanner+Unsigned.m"; sourceTree = "<group>"; };
+ 8BCB5AB011C02D7D009B6C40 /* GTMNSScanner+UnsignedTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSScanner+UnsignedTest.m"; sourceTree = "<group>"; };
8BD35C8F0FB234E1009058F5 /* GTMNSScanner+JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSScanner+JSON.h"; sourceTree = "<group>"; };
8BD35C900FB234E1009058F5 /* GTMNSScanner+JSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSScanner+JSON.m"; sourceTree = "<group>"; };
8BD35C910FB234E1009058F5 /* GTMNSScanner+JSONTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSScanner+JSONTest.m"; sourceTree = "<group>"; };
@@ -500,6 +505,9 @@
8BD35C8F0FB234E1009058F5 /* GTMNSScanner+JSON.h */,
8BD35C900FB234E1009058F5 /* GTMNSScanner+JSON.m */,
8BD35C910FB234E1009058F5 /* GTMNSScanner+JSONTest.m */,
+ 8BCB5AAE11C02D7D009B6C40 /* GTMNSScanner+Unsigned.h */,
+ 8BCB5AAF11C02D7D009B6C40 /* GTMNSScanner+Unsigned.m */,
+ 8BCB5AB011C02D7D009B6C40 /* GTMNSScanner+UnsignedTest.m */,
8BC047870DAE928A00C2D1CA /* GTMNSString+HTML.h */,
8BC047880DAE928A00C2D1CA /* GTMNSString+HTML.m */,
8BC047890DAE928A00C2D1CA /* GTMNSString+HTMLTest.m */,
@@ -832,6 +840,8 @@
0BBC768C10FEF62C0006FABE /* GTMStringEncodingTest.m in Sources */,
8BB78FA911B94D9500AB31AF /* GTMGoogleSearch.m in Sources */,
8BB78FAA11B94D9500AB31AF /* GTMGoogleSearchTest.m in Sources */,
+ 8BCB5AB111C02D7D009B6C40 /* GTMNSScanner+Unsigned.m in Sources */,
+ 8BCB5AB211C02D7D009B6C40 /* GTMNSScanner+UnsignedTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};