|
| 1 | +// |
| 2 | +// NSData+Base64.m |
| 3 | +// base64 |
| 4 | +// |
| 5 | +// Created by Matt Gallagher on 2009/06/03. |
| 6 | +// Copyright 2009 Matt Gallagher. All rights reserved. |
| 7 | +// |
| 8 | +// This software is provided 'as-is', without any express or implied |
| 9 | +// warranty. In no event will the authors be held liable for any damages |
| 10 | +// arising from the use of this software. Permission is granted to anyone to |
| 11 | +// use this software for any purpose, including commercial applications, and to |
| 12 | +// alter it and redistribute it freely, subject to the following restrictions: |
| 13 | +// |
| 14 | +// 1. The origin of this software must not be misrepresented; you must not |
| 15 | +// claim that you wrote the original software. If you use this software |
| 16 | +// in a product, an acknowledgment in the product documentation would be |
| 17 | +// appreciated but is not required. |
| 18 | +// 2. Altered source versions must be plainly marked as such, and must not be |
| 19 | +// misrepresented as being the original software. |
| 20 | +// 3. This notice may not be removed or altered from any source |
| 21 | +// distribution. |
| 22 | +// |
| 23 | + |
| 24 | +#import "NSData+Base64.h" |
| 25 | + |
| 26 | +// |
| 27 | +// Mapping from 6 bit pattern to ASCII character. |
| 28 | +// |
| 29 | +static unsigned char base64EncodeLookup[65] = |
| 30 | + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
| 31 | + |
| 32 | +// |
| 33 | +// Definition for "masked-out" areas of the base64DecodeLookup mapping |
| 34 | +// |
| 35 | +#define xx 65 |
| 36 | + |
| 37 | +// |
| 38 | +// Mapping from ASCII character to 6 bit pattern. |
| 39 | +// |
| 40 | +static unsigned char base64DecodeLookup[256] = |
| 41 | +{ |
| 42 | + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, |
| 43 | + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, |
| 44 | + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62, xx, xx, xx, 63, |
| 45 | + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, xx, xx, xx, xx, xx, xx, |
| 46 | + xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
| 47 | + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, xx, xx, xx, xx, xx, |
| 48 | + xx, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, |
| 49 | + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, xx, xx, xx, xx, xx, |
| 50 | + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, |
| 51 | + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, |
| 52 | + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, |
| 53 | + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, |
| 54 | + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, |
| 55 | + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, |
| 56 | + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, |
| 57 | + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, |
| 58 | +}; |
| 59 | + |
| 60 | +// |
| 61 | +// Fundamental sizes of the binary and base64 encode/decode units in bytes |
| 62 | +// |
| 63 | +#define BINARY_UNIT_SIZE 3 |
| 64 | +#define BASE64_UNIT_SIZE 4 |
| 65 | + |
| 66 | +// |
| 67 | +// NewBase64Decode |
| 68 | +// |
| 69 | +// Decodes the base64 ASCII string in the inputBuffer to a newly malloced |
| 70 | +// output buffer. |
| 71 | +// |
| 72 | +// inputBuffer - the source ASCII string for the decode |
| 73 | +// length - the length of the string or -1 (to specify strlen should be used) |
| 74 | +// outputLength - if not-NULL, on output will contain the decoded length |
| 75 | +// |
| 76 | +// returns the decoded buffer. Must be free'd by caller. Length is given by |
| 77 | +// outputLength. |
| 78 | +// |
| 79 | +void *NewBase64Decode( |
| 80 | + const char *inputBuffer, |
| 81 | + size_t length, |
| 82 | + size_t *outputLength) |
| 83 | +{ |
| 84 | + if (length == -1) |
| 85 | + { |
| 86 | + length = strlen(inputBuffer); |
| 87 | + } |
| 88 | + |
| 89 | + size_t outputBufferSize = |
| 90 | + ((length+BASE64_UNIT_SIZE-1) / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE; |
| 91 | + unsigned char *outputBuffer = (unsigned char *)malloc(outputBufferSize); |
| 92 | + |
| 93 | + size_t i = 0; |
| 94 | + size_t j = 0; |
| 95 | + while (i < length) |
| 96 | + { |
| 97 | + // |
| 98 | + // Accumulate 4 valid characters (ignore everything else) |
| 99 | + // |
| 100 | + unsigned char accumulated[BASE64_UNIT_SIZE]; |
| 101 | + size_t accumulateIndex = 0; |
| 102 | + while (i < length) |
| 103 | + { |
| 104 | + unsigned char decode = base64DecodeLookup[inputBuffer[i++]]; |
| 105 | + if (decode != xx) |
| 106 | + { |
| 107 | + accumulated[accumulateIndex] = decode; |
| 108 | + accumulateIndex++; |
| 109 | + |
| 110 | + if (accumulateIndex == BASE64_UNIT_SIZE) |
| 111 | + { |
| 112 | + break; |
| 113 | + } |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + // |
| 118 | + // Store the 6 bits from each of the 4 characters as 3 bytes |
| 119 | + // |
| 120 | + // (Uses improved bounds checking suggested by Alexandre Colucci) |
| 121 | + // |
| 122 | + if(accumulateIndex >= 2) |
| 123 | + outputBuffer[j] = (accumulated[0] << 2) | (accumulated[1] >> 4); |
| 124 | + if(accumulateIndex >= 3) |
| 125 | + outputBuffer[j + 1] = (accumulated[1] << 4) | (accumulated[2] >> 2); |
| 126 | + if(accumulateIndex >= 4) |
| 127 | + outputBuffer[j + 2] = (accumulated[2] << 6) | accumulated[3]; |
| 128 | + j += accumulateIndex - 1; |
| 129 | + } |
| 130 | + |
| 131 | + if (outputLength) |
| 132 | + { |
| 133 | + *outputLength = j; |
| 134 | + } |
| 135 | + return outputBuffer; |
| 136 | +} |
| 137 | + |
| 138 | +// |
| 139 | +// NewBase64Encode |
| 140 | +// |
| 141 | +// Encodes the arbitrary data in the inputBuffer as base64 into a newly malloced |
| 142 | +// output buffer. |
| 143 | +// |
| 144 | +// inputBuffer - the source data for the encode |
| 145 | +// length - the length of the input in bytes |
| 146 | +// separateLines - if zero, no CR/LF characters will be added. Otherwise |
| 147 | +// a CR/LF pair will be added every 64 encoded chars. |
| 148 | +// outputLength - if not-NULL, on output will contain the encoded length |
| 149 | +// (not including terminating 0 char) |
| 150 | +// |
| 151 | +// returns the encoded buffer. Must be free'd by caller. Length is given by |
| 152 | +// outputLength. |
| 153 | +// |
| 154 | +char *NewBase64Encode( |
| 155 | + const void *buffer, |
| 156 | + size_t length, |
| 157 | + bool separateLines, |
| 158 | + size_t *outputLength) |
| 159 | +{ |
| 160 | + const unsigned char *inputBuffer = (const unsigned char *)buffer; |
| 161 | + |
| 162 | + #define MAX_NUM_PADDING_CHARS 2 |
| 163 | + #define OUTPUT_LINE_LENGTH 64 |
| 164 | + #define INPUT_LINE_LENGTH ((OUTPUT_LINE_LENGTH / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE) |
| 165 | + #define CR_LF_SIZE 2 |
| 166 | + |
| 167 | + // |
| 168 | + // Byte accurate calculation of final buffer size |
| 169 | + // |
| 170 | + size_t outputBufferSize = |
| 171 | + ((length / BINARY_UNIT_SIZE) |
| 172 | + + ((length % BINARY_UNIT_SIZE) ? 1 : 0)) |
| 173 | + * BASE64_UNIT_SIZE; |
| 174 | + if (separateLines) |
| 175 | + { |
| 176 | + outputBufferSize += |
| 177 | + (outputBufferSize / OUTPUT_LINE_LENGTH) * CR_LF_SIZE; |
| 178 | + } |
| 179 | + |
| 180 | + // |
| 181 | + // Include space for a terminating zero |
| 182 | + // |
| 183 | + outputBufferSize += 1; |
| 184 | + |
| 185 | + // |
| 186 | + // Allocate the output buffer |
| 187 | + // |
| 188 | + char *outputBuffer = (char *)malloc(outputBufferSize); |
| 189 | + if (!outputBuffer) |
| 190 | + { |
| 191 | + return NULL; |
| 192 | + } |
| 193 | + |
| 194 | + size_t i = 0; |
| 195 | + size_t j = 0; |
| 196 | + const size_t lineLength = separateLines ? INPUT_LINE_LENGTH : length; |
| 197 | + size_t lineEnd = lineLength; |
| 198 | + |
| 199 | + while (true) |
| 200 | + { |
| 201 | + if (lineEnd > length) |
| 202 | + { |
| 203 | + lineEnd = length; |
| 204 | + } |
| 205 | + |
| 206 | + for (; i + BINARY_UNIT_SIZE - 1 < lineEnd; i += BINARY_UNIT_SIZE) |
| 207 | + { |
| 208 | + // |
| 209 | + // Inner loop: turn 48 bytes into 64 base64 characters |
| 210 | + // |
| 211 | + outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; |
| 212 | + outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4) |
| 213 | + | ((inputBuffer[i + 1] & 0xF0) >> 4)]; |
| 214 | + outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i + 1] & 0x0F) << 2) |
| 215 | + | ((inputBuffer[i + 2] & 0xC0) >> 6)]; |
| 216 | + outputBuffer[j++] = base64EncodeLookup[inputBuffer[i + 2] & 0x3F]; |
| 217 | + } |
| 218 | + |
| 219 | + if (lineEnd == length) |
| 220 | + { |
| 221 | + break; |
| 222 | + } |
| 223 | + |
| 224 | + // |
| 225 | + // Add the newline |
| 226 | + // |
| 227 | + outputBuffer[j++] = '\r'; |
| 228 | + outputBuffer[j++] = '\n'; |
| 229 | + lineEnd += lineLength; |
| 230 | + } |
| 231 | + |
| 232 | + if (i + 1 < length) |
| 233 | + { |
| 234 | + // |
| 235 | + // Handle the single '=' case |
| 236 | + // |
| 237 | + outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; |
| 238 | + outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4) |
| 239 | + | ((inputBuffer[i + 1] & 0xF0) >> 4)]; |
| 240 | + outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i + 1] & 0x0F) << 2]; |
| 241 | + outputBuffer[j++] = '='; |
| 242 | + } |
| 243 | + else if (i < length) |
| 244 | + { |
| 245 | + // |
| 246 | + // Handle the double '=' case |
| 247 | + // |
| 248 | + outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; |
| 249 | + outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0x03) << 4]; |
| 250 | + outputBuffer[j++] = '='; |
| 251 | + outputBuffer[j++] = '='; |
| 252 | + } |
| 253 | + outputBuffer[j] = 0; |
| 254 | + |
| 255 | + // |
| 256 | + // Set the output length and return the buffer |
| 257 | + // |
| 258 | + if (outputLength) |
| 259 | + { |
| 260 | + *outputLength = j; |
| 261 | + } |
| 262 | + return outputBuffer; |
| 263 | +} |
| 264 | + |
| 265 | +@implementation NSData (Base64) |
| 266 | + |
| 267 | +// |
| 268 | +// dataFromBase64String: |
| 269 | +// |
| 270 | +// Creates an NSData object containing the base64 decoded representation of |
| 271 | +// the base64 string 'aString' |
| 272 | +// |
| 273 | +// Parameters: |
| 274 | +// aString - the base64 string to decode |
| 275 | +// |
| 276 | +// returns the autoreleased NSData representation of the base64 string |
| 277 | +// |
| 278 | ++ (NSData *)dataFromBase64String:(NSString *)aString |
| 279 | +{ |
| 280 | + NSData *data = [aString dataUsingEncoding:NSASCIIStringEncoding]; |
| 281 | + size_t outputLength; |
| 282 | + void *outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength); |
| 283 | + NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength]; |
| 284 | + free(outputBuffer); |
| 285 | + return result; |
| 286 | +} |
| 287 | + |
| 288 | +// |
| 289 | +// base64EncodedString |
| 290 | +// |
| 291 | +// Creates an NSString object that contains the base 64 encoding of the |
| 292 | +// receiver's data. Lines are broken at 64 characters long. |
| 293 | +// |
| 294 | +// returns an autoreleased NSString being the base 64 representation of the |
| 295 | +// receiver. |
| 296 | +// |
| 297 | +- (NSString *)base64EncodedString |
| 298 | +{ |
| 299 | + size_t outputLength; |
| 300 | + char *outputBuffer = |
| 301 | + NewBase64Encode([self bytes], [self length], true, &outputLength); |
| 302 | + |
| 303 | + NSString *result = [[NSString alloc] initWithBytes:outputBuffer length:outputLength encoding:NSASCIIStringEncoding]; |
| 304 | + free(outputBuffer); |
| 305 | + return result; |
| 306 | +} |
| 307 | + |
| 308 | +@end |
0 commit comments