Permalink
Browse files

Added Base64 decoding. Fixes #2

  • Loading branch information...
1 parent f63bf11 commit 2ad922a5d53a30089aeba814ac4e354374c71108 @soffes soffes committed Jul 10, 2011
View
2 SSToolkit.xcodeproj/project.pbxproj
@@ -107,7 +107,6 @@
B28219DE12FA8EB500BAF3C6 /* SSRatingPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = B28219DC12FA8EB500BAF3C6 /* SSRatingPicker.m */; };
B28C6D0712FBE5C400667755 /* SSRatingPickerViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = B28C6D0512FBE5C400667755 /* SSRatingPickerViewController.h */; };
B28C6D0812FBE5C400667755 /* SSRatingPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B28C6D0612FBE5C400667755 /* SSRatingPickerViewController.m */; };
- B290355A13B9236E00CCAFC9 /* SSAnimatedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = B290355913B9236E00CCAFC9 /* SSAnimatedImageView.m */; };
B2AAE65C1281B73E0068EE7F /* SSNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = B2AAE65A1281B73E0068EE7F /* SSNavigationController.h */; };
B2AAE65D1281B73E0068EE7F /* SSNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = B2AAE65B1281B73E0068EE7F /* SSNavigationController.m */; };
B2ABC6921329D14300521D20 /* SSCollectionViewItemTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = B2ABC6901329D14300521D20 /* SSCollectionViewItemTableViewCell.h */; };
@@ -722,7 +721,6 @@
B2453A1D1394AE5000275B6B /* DictionaryCategoryTest.m in Sources */,
B2453A1E1394AE5000275B6B /* StringCategoryTest.m in Sources */,
B2453A1F1394AE5000275B6B /* URLCategoryTest.m in Sources */,
- B290355A13B9236E00CCAFC9 /* SSAnimatedImageView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
7 SSToolkit/NSData+SSToolkitAdditions.h
@@ -18,4 +18,11 @@
*/
- (NSString *)MD5Sum;
+///-----------------------------------
+/// @name Base64 Encoding and Decoding
+///-----------------------------------
+
+- (NSString *)base64EncodedString;
++ (NSData *)dataWithBase64String:(NSString *)base64String;
+
@end
View
87 SSToolkit/NSData+SSToolkitAdditions.m
@@ -9,6 +9,26 @@
#import "NSData+SSToolkitAdditions.h"
#include <CommonCrypto/CommonDigest.h>
+static const char _base64EncodingTable[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const short _base64DecodingTable[256] = {
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -1, -1, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+ -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2,
+ -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
+ -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
+};
+
@implementation NSData (SSToolkitAdditions)
- (NSString *)MD5Sum {
@@ -21,4 +41,71 @@ - (NSString *)MD5Sum {
return [[ms copy] autorelease];
}
+
+// Adapted from http://www.cocoadev.com/index.pl?BaseSixtyFour
+- (NSString *)base64EncodedString {
+ const uint8_t *input = self.bytes;
+ NSInteger length = self.length;
+
+ NSMutableData *data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
+ uint8_t *output = (uint8_t *)data.mutableBytes;
+
+ for (NSInteger i = 0; i < length; i += 3) {
+ NSInteger value = 0;
+ for (NSInteger j = i; j < (i + 3); j++) {
+ value <<= 8;
+
+ if (j < length) {
+ value |= (0xFF & input[j]);
+ }
+ }
+
+ NSInteger index = (i / 3) * 4;
+ output[index + 0] = _base64EncodingTable[(value >> 18) & 0x3F];
+ output[index + 1] = _base64EncodingTable[(value >> 12) & 0x3F];
+ output[index + 2] = (i + 1) < length ? _base64EncodingTable[(value >> 6) & 0x3F] : '=';
+ output[index + 3] = (i + 2) < length ? _base64EncodingTable[(value >> 0) & 0x3F] : '=';
+ }
+
+ return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
+}
+
+
+// Adapted from http://www.cocoadev.com/index.pl?BaseSixtyFour
++ (NSData *)dataWithBase64String:(NSString *)base64String {
+ const char *string = [base64String cStringUsingEncoding:NSASCIIStringEncoding];
+ NSInteger inputLength = base64String.length;
+
+ if (string == NULL/* || inputLength % 4 != 0*/) {
+ return nil;
+ }
+
+ while (inputLength > 0 && string[inputLength - 1] == '=') {
+ inputLength--;
+ }
+
+ NSInteger outputLength = inputLength * 3 / 4;
+ NSMutableData* data = [NSMutableData dataWithLength:outputLength];
+ uint8_t *output = data.mutableBytes;
+
+ NSInteger inputPoint = 0;
+ NSInteger outputPoint = 0;
+ while (inputPoint < inputLength) {
+ char i0 = string[inputPoint++];
+ char i1 = string[inputPoint++];
+ char i2 = inputPoint < inputLength ? string[inputPoint++] : 'A'; /* 'A' will decode to \0 */
+ char i3 = inputPoint < inputLength ? string[inputPoint++] : 'A';
+
+ output[outputPoint++] = (_base64DecodingTable[i0] << 2) | (_base64DecodingTable[i1] >> 4);
+ if (outputPoint < outputLength) {
+ output[outputPoint++] = ((_base64DecodingTable[i1] & 0xf) << 4) | (_base64DecodingTable[i2] >> 2);
+ }
+ if (outputPoint < outputLength) {
+ output[outputPoint++] = ((_base64DecodingTable[i2] & 0x3) << 6) | _base64DecodingTable[i3];
+ }
+ }
+
+ return data;
+}
+
@end
View
1 SSToolkit/NSString+SSToolkitAdditions.h
@@ -53,6 +53,7 @@
///----------------------
- (NSString *)base64EncodedString;
++ (NSString *)stringWithBase64String:(NSString *)base64String;
///---------------
/// @name Trimming
View
50 SSToolkit/NSString+SSToolkitAdditions.m
@@ -7,9 +7,8 @@
//
#import "NSString+SSToolkitAdditions.h"
-#include <CommonCrypto/CommonDigest.h>
-
-static const char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+#import "NSData+SSToolkitAdditions.h"
+#import <CommonCrypto/CommonDigest.h>
@implementation NSString (SSToolkitAdditions)
@@ -77,7 +76,7 @@ - (NSComparisonResult)compareToVersionString:(NSString *)version {
}
-#pragma mark HTML Methods
+#pragma mark - HTML Methods
- (NSString *)escapeHTML {
NSMutableString *s = [NSMutableString string];
@@ -236,49 +235,24 @@ - (NSString*)stringByUnescapingFromURLQuery {
}
-#pragma mark Base64 Methods
+#pragma mark - Base64 Methods
- (NSString *)base64EncodedString {
if ([self length] == 0) {
- return @"";
- }
-
- const char *source = [self UTF8String];
- NSUInteger strlength = strlen(source);
-
- char *characters = malloc(((strlength + 2) / 3) * 4);
- if (characters == NULL) {
return nil;
}
- NSUInteger length = 0;
- NSUInteger i = 0;
-
- while (i < strlength) {
- char buffer[3] = {0,0,0};
- short bufferLength = 0;
- while (bufferLength < 3 && i < strlength) {
- buffer[bufferLength++] = source[i++];
- }
- characters[length++] = encodingTable[(buffer[0] & 0xFC) >> 2];
- characters[length++] = encodingTable[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)];
- if (bufferLength > 1) {
- characters[length++] = encodingTable[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)];
- } else {
- characters[length++] = '=';
- }
- if (bufferLength > 2) {
- characters[length++] = encodingTable[buffer[2] & 0x3F];
- } else {
- characters[length++] = '=';
- }
- }
-
- return [[[NSString alloc] initWithBytesNoCopy:characters length:length encoding:NSASCIIStringEncoding freeWhenDone:YES] autorelease];
+ return [[self dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString];
+}
+
+
++ (NSString *)stringWithBase64String:(NSString *)base64String {
+ return [[[NSString alloc] initWithData:[NSData dataWithBase64String:base64String] encoding:NSUTF8StringEncoding]
+ autorelease];
}
-#pragma mark Trimming Methods
+#pragma mark - Trimming Methods
- (NSString *)stringByTrimmingLeadingCharactersInSet:(NSCharacterSet *)characterSet {
NSRange rangeOfFirstWantedCharacter = [self rangeOfCharacterFromSet:[characterSet invertedSet]];
View
21 Tests/DataCategoryTest.m
@@ -22,4 +22,25 @@ - (void)testMD5Sum {
GHAssertEqualObjects([data MD5Sum], @"fa5c89f3c88b81bfd5e821b0316569af", nil);
}
+
+- (void)testBase64 {
+ NSString *unencodedString = @"sam";
+ NSString *encodedString = @"c2Ft";
+ NSData *unencodedData = [unencodedString dataUsingEncoding:NSUTF8StringEncoding];
+ GHAssertEqualObjects(encodedString, [unencodedData base64EncodedString], nil);
+ GHAssertEqualObjects(unencodedData, [NSData dataWithBase64String:encodedString], nil);
+
+ unencodedString = @"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+ encodedString = @"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2ljaW5nIGVsaXQsIHNlZCBkbyBlaXVzbW9kIHRlbXBvciBpbmNpZGlkdW50IHV0IGxhYm9yZSBldCBkb2xvcmUgbWFnbmEgYWxpcXVhLiBVdCBlbmltIGFkIG1pbmltIHZlbmlhbSwgcXVpcyBub3N0cnVkIGV4ZXJjaXRhdGlvbiB1bGxhbWNvIGxhYm9yaXMgbmlzaSB1dCBhbGlxdWlwIGV4IGVhIGNvbW1vZG8gY29uc2VxdWF0LiBEdWlzIGF1dGUgaXJ1cmUgZG9sb3IgaW4gcmVwcmVoZW5kZXJpdCBpbiB2b2x1cHRhdGUgdmVsaXQgZXNzZSBjaWxsdW0gZG9sb3JlIGV1IGZ1Z2lhdCBudWxsYSBwYXJpYXR1ci4gRXhjZXB0ZXVyIHNpbnQgb2NjYWVjYXQgY3VwaWRhdGF0IG5vbiBwcm9pZGVudCwgc3VudCBpbiBjdWxwYSBxdWkgb2ZmaWNpYSBkZXNlcnVudCBtb2xsaXQgYW5pbSBpZCBlc3QgbGFib3J1bS4=";
+ unencodedData = [unencodedString dataUsingEncoding:NSUTF8StringEncoding];
+ GHAssertEqualObjects(encodedString, [unencodedData base64EncodedString], nil);
+ GHAssertEqualObjects(unencodedData, [NSData dataWithBase64String:encodedString], nil);
+
+ unencodedString = @"http://www.cocoadev.com/index.pl?BaseSixtyFour";
+ encodedString = @"aHR0cDovL3d3dy5jb2NvYWRldi5jb20vaW5kZXgucGw/QmFzZVNpeHR5Rm91cg==";
+ unencodedData = [unencodedString dataUsingEncoding:NSUTF8StringEncoding];
+ GHAssertEqualObjects(encodedString, [unencodedData base64EncodedString], nil);
+ GHAssertEqualObjects(unencodedData, [NSData dataWithBase64String:encodedString], nil);
+}
+
@end
View
17 Tests/StringCategoryTest.m
@@ -54,11 +54,20 @@ - (void)testCompareToVersionString {
- (void)testBase64EncodedString {
- GHAssertEqualObjects([@"sam" base64EncodedString], @"c2Ft", nil);
+ NSString *unencodedString = @"sam";
+ NSString *encodedString = @"c2Ft";
+ GHAssertEqualObjects(encodedString, [unencodedString base64EncodedString], nil);
+ GHAssertEqualObjects(unencodedString, [NSString stringWithBase64String:encodedString], nil);
- NSString *lorem = @"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
- NSString *encodedLorem = @"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2ljaW5nIGVsaXQsIHNlZCBkbyBlaXVzbW9kIHRlbXBvciBpbmNpZGlkdW50IHV0IGxhYm9yZSBldCBkb2xvcmUgbWFnbmEgYWxpcXVhLiBVdCBlbmltIGFkIG1pbmltIHZlbmlhbSwgcXVpcyBub3N0cnVkIGV4ZXJjaXRhdGlvbiB1bGxhbWNvIGxhYm9yaXMgbmlzaSB1dCBhbGlxdWlwIGV4IGVhIGNvbW1vZG8gY29uc2VxdWF0LiBEdWlzIGF1dGUgaXJ1cmUgZG9sb3IgaW4gcmVwcmVoZW5kZXJpdCBpbiB2b2x1cHRhdGUgdmVsaXQgZXNzZSBjaWxsdW0gZG9sb3JlIGV1IGZ1Z2lhdCBudWxsYSBwYXJpYXR1ci4gRXhjZXB0ZXVyIHNpbnQgb2NjYWVjYXQgY3VwaWRhdGF0IG5vbiBwcm9pZGVudCwgc3VudCBpbiBjdWxwYSBxdWkgb2ZmaWNpYSBkZXNlcnVudCBtb2xsaXQgYW5pbSBpZCBlc3QgbGFib3J1bS4=";
- GHAssertEqualObjects([lorem base64EncodedString], encodedLorem, nil);
+ unencodedString = @"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+ encodedString = @"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2ljaW5nIGVsaXQsIHNlZCBkbyBlaXVzbW9kIHRlbXBvciBpbmNpZGlkdW50IHV0IGxhYm9yZSBldCBkb2xvcmUgbWFnbmEgYWxpcXVhLiBVdCBlbmltIGFkIG1pbmltIHZlbmlhbSwgcXVpcyBub3N0cnVkIGV4ZXJjaXRhdGlvbiB1bGxhbWNvIGxhYm9yaXMgbmlzaSB1dCBhbGlxdWlwIGV4IGVhIGNvbW1vZG8gY29uc2VxdWF0LiBEdWlzIGF1dGUgaXJ1cmUgZG9sb3IgaW4gcmVwcmVoZW5kZXJpdCBpbiB2b2x1cHRhdGUgdmVsaXQgZXNzZSBjaWxsdW0gZG9sb3JlIGV1IGZ1Z2lhdCBudWxsYSBwYXJpYXR1ci4gRXhjZXB0ZXVyIHNpbnQgb2NjYWVjYXQgY3VwaWRhdGF0IG5vbiBwcm9pZGVudCwgc3VudCBpbiBjdWxwYSBxdWkgb2ZmaWNpYSBkZXNlcnVudCBtb2xsaXQgYW5pbSBpZCBlc3QgbGFib3J1bS4=";
+ GHAssertEqualObjects(encodedString, [unencodedString base64EncodedString], nil);
+ GHAssertEqualObjects(unencodedString, [NSString stringWithBase64String:encodedString], nil);
+
+ unencodedString = @"http://www.cocoadev.com/index.pl?BaseSixtyFour";
+ encodedString = @"aHR0cDovL3d3dy5jb2NvYWRldi5jb20vaW5kZXgucGw/QmFzZVNpeHR5Rm91cg==";
+ GHAssertEqualObjects(encodedString, [unencodedString base64EncodedString], nil);
+ GHAssertEqualObjects(unencodedString, [NSString stringWithBase64String:encodedString], nil);
}

0 comments on commit 2ad922a

Please sign in to comment.