diff options
-rw-r--r-- | Foundation/GTMNSScanner+Unsigned.h | 30 | ||||
-rw-r--r-- | Foundation/GTMNSScanner+Unsigned.m | 60 | ||||
-rw-r--r-- | Foundation/GTMNSScanner+UnsignedTest.m | 116 | ||||
-rw-r--r-- | GTM.xcodeproj/project.pbxproj | 12 | ||||
-rw-r--r-- | GTMiPhone.xcodeproj/project.pbxproj | 10 |
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; }; |