/* Copyright (c) 2012 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 "GTLBase64.h" // Based on Cyrus Najmabadi's elegent little encoder and decoder from // http://www.cocoadev.com/index.pl?BaseSixtyFour static char gStandardEncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static char gWebSafeEncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; #pragma mark Encode static NSString *EncodeBase64StringCommon(NSData *data, const char *table) { if (data == nil) return nil; const uint8_t* input = [data bytes]; NSUInteger length = [data length]; NSUInteger bufferSize = ((length + 2) / 3) * 4; NSMutableData* buffer = [NSMutableData dataWithLength:bufferSize]; uint8_t *output = [buffer mutableBytes]; for (NSUInteger i = 0; i < length; i += 3) { NSUInteger value = 0; for (NSUInteger j = i; j < (i + 3); j++) { value <<= 8; if (j < length) { value |= (0xFF & input[j]); } } NSInteger idx = (i / 3) * 4; output[idx + 0] = table[(value >> 18) & 0x3F]; output[idx + 1] = table[(value >> 12) & 0x3F]; output[idx + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '='; output[idx + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '='; } NSString *result = [[[NSString alloc] initWithData:buffer encoding:NSASCIIStringEncoding] autorelease]; return result; } NSString *GTLEncodeBase64(NSData *data) { return EncodeBase64StringCommon(data, gStandardEncodingTable); } NSString *GTLEncodeWebSafeBase64(NSData *data) { return EncodeBase64StringCommon(data, gWebSafeEncodingTable); } #pragma mark Decode static void CreateDecodingTable(const char *encodingTable, size_t encodingTableSize, char *decodingTable) { memset(decodingTable, 0, 128); for (unsigned int i = 0; i < encodingTableSize; i++) { decodingTable[(unsigned int) encodingTable[i]] = i; } } static NSData *DecodeBase64StringCommon(NSString *base64Str, char *decodingTable) { // The input string should be plain ASCII const char *cString = [base64Str cStringUsingEncoding:NSASCIIStringEncoding]; if (cString == nil) return nil; NSUInteger inputLength = strlen(cString); if (inputLength % 4 != 0) return nil; if (inputLength == 0) return [NSData data]; while (inputLength > 0 && cString[inputLength - 1] == '=') { inputLength--; } NSInteger outputLength = inputLength * 3 / 4; NSMutableData* data = [NSMutableData dataWithLength:outputLength]; uint8_t *output = [data mutableBytes]; NSInteger inputPoint = 0; NSInteger outputPoint = 0; char *table = decodingTable; while (inputPoint < inputLength) { int i0 = cString[inputPoint++]; int i1 = cString[inputPoint++]; int i2 = inputPoint < inputLength ? cString[inputPoint++] : 'A'; // 'A' will decode to \0 int i3 = inputPoint < inputLength ? cString[inputPoint++] : 'A'; output[outputPoint++] = (table[i0] << 2) | (table[i1] >> 4); if (outputPoint < outputLength) { output[outputPoint++] = ((table[i1] & 0xF) << 4) | (table[i2] >> 2); } if (outputPoint < outputLength) { output[outputPoint++] = ((table[i2] & 0x3) << 6) | table[i3]; } } return data; } NSData *GTLDecodeBase64(NSString *base64Str) { static char decodingTable[128]; static BOOL hasInited = NO; if (!hasInited) { CreateDecodingTable(gStandardEncodingTable, sizeof(gStandardEncodingTable), decodingTable); hasInited = YES; } return DecodeBase64StringCommon(base64Str, decodingTable); } NSData *GTLDecodeWebSafeBase64(NSString *base64Str) { static char decodingTable[128]; static BOOL hasInited = NO; if (!hasInited) { CreateDecodingTable(gWebSafeEncodingTable, sizeof(gWebSafeEncodingTable), decodingTable); hasInited = YES; } return DecodeBase64StringCommon(base64Str, decodingTable); }