1// 2// GSNSDataExtensions.m 3// 4// Created by khammond on Mon Oct 29 2001. 5// Copyright (c) 2001, 2005 Kyle Hammond. All rights reserved. 6// 7// Original development comments by Dave Winer. 8// Jan 12, 2005 - added AltiVec implementation, and greatly improved encoding speed. 9 10/* C source code for Base 64 11 12 Here's the C source code for the Base 64 encoder/decoder. 13 14 File: 15 base64.c 16 Created: 17 Saturday, April 5, 1997; 1:30:13 PM 18 Modified: 19 Tuesday, April 8, 1997; 7:52:28 AM 20 21 Dave Winer, dwiner@well.com, UserLand Software, 4/7/97 22 23 I built this project using Symantec C++ 7.0.4 on a Mac 9500. 24 25 We needed a handle-based Base 64 encoder/decoder. Looked around the 26 net, found a bunch of code that couldn't easily be adapted to 27 in-memory stuff. Most of them work on files to conserve memory. This 28 is inelegant in scripting environments such as Frontier. 29 30 Anyway, so I wrote an encoder/decoder. Docs are being maintained 31 on the web, and updates at: 32 33 http://www.scripting.com/midas/base64/ 34 35 If you port this code to another platform please put the result up 36 on a website, and send me a pointer. Also send email if you think this 37 isn't a compatible implementation of Base 64 encoding. 38 39 BTW, I made it easy to port -- layering out the handle access routines. 40 Of course there's a small performance penalty for this, and if you don't 41 like it, change it. Thanks! 42 */ 43 44#import "GSNSDataExtensions.h" 45 46// Comment this out (or change it to a zero) to disable AltiVec processing. 47#define COMPILE_FOR_ALTIVEC 0 48 49static unsigned long local_preprocessForDecode( const unsigned char *inBytes, unsigned long inBytesLength, unsigned char *outData ); 50 51#if defined( COMPILE_FOR_ALTIVEC ) && ( COMPILE_FOR_ALTIVEC == 1 ) 52 53static BOOL local_AltiVec_IsPresent( void ); 54static unsigned long local_altiVec_encode( unsigned char *inBytes, unsigned long inBytesLength, unsigned char *outData, unsigned long *outEncodedLength ); 55static unsigned long local_altiVec_preprocessForDecode( const unsigned char *inBytes, unsigned long inBytesLength, unsigned char *outData ); 56static unsigned long local_altiVec_decode( unsigned char *inBytes, unsigned long inBytesLength, unsigned char *outData, unsigned long *outDecodedLength ); 57 58#endif 59 60@implementation NSData (Base64Encoding) 61 62static char gEncodingTable[ 64 ] = { 63 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 64 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', 65 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', 66 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' 67 }; 68 69+ (BOOL)isCharacterPartOfBase64Encoding:(char)inChar 70{ 71 int i; 72 73 for ( i = 0; i < 64; i++ ) 74 { 75 if ( gEncodingTable[ i ] == inChar ) 76 return YES; 77 } 78 79 return NO; 80} 81 82+ (NSData *)dataWithBase64EncodedString:(NSString *)inBase64String 83{ 84 NSData *result = nil; 85 86 result = [ [ NSData alloc ] initWithBase64EncodedString:inBase64String ]; 87 88 return [ result autorelease ]; 89} 90 91- (id)initWithBase64EncodedString:(NSString *)inBase64String 92{ 93 NSMutableData *mutableData = nil; 94 95 if ( inBase64String && [ inBase64String length ] > 0 ) 96 { 97 unsigned long ixtext; 98 unsigned long lentext; 99 unsigned char ch; 100 unsigned char inbuf [4], outbuf [3]; 101 short ixinbuf; 102 NSData *base64Data; 103 unsigned char *preprocessed, *decodedBytes; 104 unsigned long preprocessedLength, decodedLength; 105 short ctcharsinbuf = 3; 106 BOOL notDone = YES; 107 108 // Convert the string to ASCII data. 109 base64Data = [ inBase64String dataUsingEncoding:NSASCIIStringEncoding ]; 110 lentext = [ base64Data length ]; 111 112 preprocessed = malloc( lentext ); // We may have all valid data! 113 114 // Allocate our outbound data, and set it's length. 115 // Do this so we can fill it in without allocating memory in small chunks later. 116 mutableData = [ NSMutableData dataWithCapacity:( lentext * 3 ) / 4 + 3 ]; 117 [ mutableData setLength:( lentext * 3 ) / 4 + 3 ]; 118 decodedBytes = [ mutableData mutableBytes ]; 119 120#if defined( COMPILE_FOR_ALTIVEC ) && ( COMPILE_FOR_ALTIVEC == 1 ) 121 if ( lentext > 15 && local_AltiVec_IsPresent( ) ) 122 { 123 preprocessedLength = local_altiVec_preprocessForDecode( [ base64Data bytes ], lentext, preprocessed ); 124 ixtext = local_altiVec_decode( preprocessed, preprocessedLength, decodedBytes, 125 &decodedLength ); 126 } 127 else 128#endif // end COMPILE_FOR_ALTIVEC 129 { 130 preprocessedLength = local_preprocessForDecode( [ base64Data bytes ], lentext, preprocessed ); 131 decodedLength = 0; 132 ixtext = 0; 133 } 134 135 ixinbuf = 0; 136 137 while ( notDone && ixtext < preprocessedLength ) 138 { 139 ch = preprocessed[ ixtext++ ]; 140 141 if ( 255 == ch ) // Hit our stop signal. 142 { 143 if (ixinbuf == 0) 144 break; // We're done now! 145 146 else if ((ixinbuf == 1) || (ixinbuf == 2)) 147 { 148 ctcharsinbuf = 1; 149 ixinbuf = 3; 150 } 151 else 152 ctcharsinbuf = 2; 153 154 notDone = NO; // We're finished after the outbuf gets copied this time. 155 } 156 157 inbuf [ixinbuf++] = ch; 158 159 if ( 4 == ixinbuf ) 160 { 161 ixinbuf = 0; 162 163 outbuf [0] = (inbuf [0] << 2) | ((inbuf [1] & 0x30) >> 4); 164 165 outbuf [1] = ((inbuf [1] & 0x0F) << 4) | ((inbuf [2] & 0x3C) >> 2); 166 167 outbuf [2] = ((inbuf [2] & 0x03) << 6) | inbuf [3]; 168 169 memcpy( &decodedBytes[ decodedLength ], outbuf, ctcharsinbuf ); 170 decodedLength += ctcharsinbuf; 171 } 172 } // end while loop on remaining characters 173 174 free( preprocessed ); 175 176 // Adjust length down to however many bytes we actually decoded. 177 [ mutableData setLength:decodedLength ]; 178 } 179 180 self = [ self initWithData:mutableData ]; 181 182 return self; 183} 184 185- (NSString *)base64EncodingWithLineLength:(unsigned int)inLineLength 186{ /* 187 Encode the NSData. Some funny stuff about linelength -- it only makes 188 sense to make it a multiple of 4. If it's not a multiple of 4, we make it 189 so (by only checking it every 4 characters). 190 191 Further, if it's 0, we don't add any line breaks at all. 192 */ 193 194 const unsigned char *bytes = [ self bytes ]; 195 unsigned char *encodedData; 196 unsigned long encodedLength; 197 unsigned long ixtext; 198 unsigned long lengthData; 199 long ctremaining; 200 unsigned char inbuf [4], outbuf [3]; 201 short i; 202 short charsonline = 0, ctcopy; 203 unsigned long ix; 204 NSString *result = nil; 205 206 lengthData = [ self length ]; 207 208 if ( inLineLength > 0 ) 209 // Allocate a buffer large enough to hold everything + line endings. 210 encodedData = malloc( ( ( ( lengthData + 1 ) * 4 ) / 3 ) + ( ( ( ( lengthData + 1 ) * 4 ) / 3 ) / inLineLength ) + 1 ); 211 else 212 // Allocate a buffer large enough to hold everything. 213 encodedData = malloc( ( ( lengthData + 1 ) * 4 ) / 3 ); 214 215#if defined( COMPILE_FOR_ALTIVEC ) && ( COMPILE_FOR_ALTIVEC == 1 ) 216 if ( lengthData > 12 && local_AltiVec_IsPresent( ) ) 217 { 218 ixtext = local_altiVec_encode( (unsigned char *)bytes, lengthData, encodedData, &encodedLength ); 219 220 // Add line endings because the AltiVec algorithm doesn't do that. 221 if ( inLineLength > 0 ) 222 { 223 for ( ctremaining = inLineLength; ctremaining < encodedLength; ctremaining += inLineLength ) 224 { 225 // Since dst and src overlap here, use memmove instead of memcpy. 226 memmove( &encodedData[ ctremaining + 1 ], &encodedData[ ctremaining ], 227 encodedLength - ctremaining ); 228 encodedData[ ctremaining ] = '\n'; 229 ctremaining++; 230 encodedLength++; 231 } 232 233 // Do we need one more line ending at the very end of the string? 234 if ( ctremaining == encodedLength ) 235 { 236 encodedData[ ctremaining ] = '\n'; 237 encodedLength++; 238 } 239 else 240 // If not, we have some characters on the line. 241 charsonline = encodedLength - ( ctremaining - inLineLength ); 242 } 243 } 244 else 245#endif // end COMPILE_FOR_ALTIVEC 246 { 247 // We can't do anything with AltiVec. Do it all by standard algorithm. 248 ixtext = 0; 249 encodedLength = 0; 250 } 251 252 ctcopy = 4; 253 254 while ( YES ) 255 { 256 ctremaining = lengthData - ixtext; 257 258 if ( ctremaining >= 4 ) 259 // Copy next four bytes into inbuf. 260 (*(unsigned long *)inbuf) = *(unsigned long *)&bytes[ ixtext ]; 261 262 else if ( ctremaining <= 0 ) 263 break; 264 265 else 266 { 267 // Have less than four bytes to copy. Fill extras with zero. 268 for ( i = 0; i < 3; i++ ) 269 { 270 ix = ixtext + i; 271 272 if (ix < lengthData) 273 inbuf [i] = bytes[ix]; 274 else 275 inbuf [i] = 0; 276 } // for loop 277 278 switch ( ctremaining ) 279 { 280 case 1: 281 ctcopy = 2; 282 break; 283 284 case 2: 285 ctcopy = 3; 286 break; 287 } // switch 288 } 289 290 outbuf [0] = (inbuf [0] & 0xFC) >> 2; 291 292 outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4); 293 294 outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6); 295 296 outbuf [3] = inbuf [2] & 0x3F; 297 298 // Depending on how many characters we're supposed to copy, fill in with '=' characters. 299 if ( 4 == ctcopy ) 300 { 301 encodedData[ encodedLength++ ] = gEncodingTable[ outbuf[0] ]; 302 encodedData[ encodedLength++ ] = gEncodingTable[ outbuf[1] ]; 303 encodedData[ encodedLength++ ] = gEncodingTable[ outbuf[2] ]; 304 encodedData[ encodedLength++ ] = gEncodingTable[ outbuf[3] ]; 305 } 306 else if ( 3 == ctcopy ) 307 { 308 encodedData[ encodedLength++ ] = gEncodingTable[ outbuf[0] ]; 309 encodedData[ encodedLength++ ] = gEncodingTable[ outbuf[1] ]; 310 encodedData[ encodedLength++ ] = gEncodingTable[ outbuf[2] ]; 311 encodedData[ encodedLength++ ] = '='; 312 } 313 else 314 { 315 encodedData[ encodedLength++ ] = gEncodingTable[ outbuf[0] ]; 316 encodedData[ encodedLength++ ] = gEncodingTable[ outbuf[1] ]; 317 encodedData[ encodedLength++ ] = '='; 318 encodedData[ encodedLength++ ] = '='; 319 } 320 321 ixtext += 3; 322 323 if ( inLineLength > 0 ) 324 { // DW 4/8/97 -- 0 means no line breaks 325 326 charsonline += 4; 327 if (charsonline >= inLineLength) 328 { 329 charsonline = 0; 330 331 encodedData[ encodedLength++ ] = '\n'; 332 } 333 } 334 } // end while loop 335 336 // Make a string object out of the encoded data buffer. 337 result = (NSString *)CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, encodedData, encodedLength, kCFStringEncodingASCII, NO, kCFAllocatorMalloc); 338 339 return [result autorelease]; 340} 341 342@end 343 344#if defined( COMPILE_FOR_ALTIVEC ) && ( COMPILE_FOR_ALTIVEC == 1 ) 345 346static BOOL local_AltiVec_IsPresent( void ) 347{ 348 long cpuAttributes; 349 BOOL result = NO; 350 OSErr err; 351 352 err = Gestalt( gestaltNativeCPUtype, &cpuAttributes ); 353 if ( noErr == err && cpuAttributes > gestaltCPU750 ) 354 { 355 // Only get in here if we're greater than a G3 processor. 356 err = Gestalt( gestaltPowerPCProcessorFeatures, &cpuAttributes ); 357 if ( noErr == err ) 358 result = ( 0 != ( ( 1 << gestaltPowerPCHasVectorInstructions ) & cpuAttributes ) ); 359 } 360 361 return result; 362} 363 364static unsigned long local_altiVec_encode( unsigned char *inBytes, unsigned long inBytesLength, unsigned char *outData, unsigned long *outEncodedLength ) 365{ 366 unsigned char *finishedPtr; 367 unsigned long result = ( inBytesLength / 12 ) * 12; // round down to nearest multiple of 12 368 vector unsigned char *outboundData = (vector unsigned char *)outData; 369 vector unsigned char workingVec, shiftedLeft_lowerHalf; 370 vector bool char comparison; 371 const vector unsigned char kZero = { 0 }; 372 const vector unsigned char kPermuteInbound = (vector unsigned char)( 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 8, 9, 10, 11, 11 ); 373 const vector unsigned char kShiftRight = (vector unsigned char)( 2, 4, 6, 0, 2, 4, 6, 0, 2, 4, 6, 0, 2, 4, 6, 0 ); 374 const vector unsigned char kShiftLeft = (vector unsigned char)( 4, 2, 0, 0, 4, 2, 0, 0, 4, 2, 0, 0, 4, 2, 0, 0 ); 375 const vector unsigned char kPermuteShiftedLeft = (vector unsigned char)( 16, 0, 1, 16, 16, 4, 5, 16, 16, 8, 9, 16, 16, 12, 13, 16 ); 376 const vector unsigned char kSelectShiftedRight = (vector unsigned char)( 255, 255, 255, 63, 255, 255, 255, 63, 255, 255, 255, 63, 255, 255, 255, 63 ); 377 const vector unsigned char kSelectShiftedLeft = (vector unsigned char)( 0, 48, 60, 0, 0, 48, 60, 0, 0, 48, 60, 0, 0, 48, 60, 0 ); 378 const vector unsigned char kBase64LookupTable0 =(vector unsigned char)( 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P' ); 379 const vector unsigned char kBase64LookupTable1 = (vector unsigned char)( 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f' ); 380 const vector unsigned char kBase64LookupTable2 = (vector unsigned char)( 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v' ); 381 const vector unsigned char kBase64LookupTable3 = (vector unsigned char)( 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' ); 382 const vector unsigned char kThirtyTwo = (vector unsigned char)( 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 ); 383 384 // Determine when we will stop looping. 385 // We only do multiples of twelve inbound bytes at a time. 386 // Any extra data will need to be put into Base64 using the non-AltiVec algorithm. 387 finishedPtr = inBytes + result; 388 389 while ( inBytes < finishedPtr ) 390 { 391 // Copy the twelve bytes into a vector, then 392 // permute the inbound data into the correct bytes for output. 393 // Note that to save some CPU cycles we copy three longs (at four bytes each). 394 ((unsigned long *)&workingVec)[ 0 ] = ((unsigned long *)inBytes)[ 0 ]; 395 ((unsigned long *)&workingVec)[ 1 ] = ((unsigned long *)inBytes)[ 1 ]; 396 ((unsigned long *)&workingVec)[ 2 ] = ((unsigned long *)inBytes)[ 2 ]; 397 398 // Shift the pointer to work on the next twelve inbound data bytes. 399 inBytes += 12; 400 401 workingVec = vec_perm( workingVec, kZero, kPermuteInbound ); 402 403 // Shift some bits left and permute them into the correct byte positions. 404 // Do this first, because the vec_perm instruction can work at the same time as the next vec_sr. 405 shiftedLeft_lowerHalf = vec_perm( vec_sl( workingVec, kShiftLeft ), kZero, kPermuteShiftedLeft ); 406 407 // Shift some bits right. 408 // Select the parts of the right shifted and left shifted vectors we want. 409 workingVec = vec_sel( vec_sel( kZero, vec_sr( workingVec, kShiftRight ), kSelectShiftedRight ), shiftedLeft_lowerHalf, kSelectShiftedLeft ); 410 411 // As of now, we have 16 indices ranging from 0 to 63 in workingVec. 412 413 // Determine which indices will use the lower half of the lookup table and which the upper half. 414 comparison = vec_cmplt( workingVec, kThirtyTwo ); 415 416 // Do the table lookup. 417 // Recombine the characters from the upper and lower half lookups into the outbound data. 418 (*outboundData) = vec_sel( 419 vec_perm( kBase64LookupTable2, kBase64LookupTable3, workingVec ), 420 vec_perm( kBase64LookupTable0, kBase64LookupTable1, workingVec ), 421 comparison ); 422 423 // Shift the pointer to work on the next set of sixteen outbound data bytes. 424 outboundData++; 425 } 426 427 // Specify how much data we created. 428 *outEncodedLength = ((unsigned char *)outboundData - outData); 429 430 // Return how much of the inbound data we encoded. 431 return result; 432} 433 434static unsigned long local_altiVec_preprocessForDecode( const unsigned char *inBytes, unsigned long inBytesLength, unsigned char *outData ) 435{ 436 unsigned long i; 437 BOOL foundAnEqual = NO; 438 unsigned char *outboundData = outData; 439 unsigned char *inboundData = (unsigned char *)inBytes; 440 unsigned char *finishedPtr = (unsigned char *)inBytes + inBytesLength; 441 vector unsigned char workingVec, finishedVec; 442 vector bool char foundCaps, foundLower, foundNum, foundPlus, foundSlash, foundEqual; 443 vector unsigned char kOne; 444 const vector unsigned char kCharAlpha_Cap = (vector unsigned char)( 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1, 'A' - 1 ); 445 const vector unsigned char kCharZed_Cap = (vector unsigned char)( 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1, 'Z' + 1 ); 446 const vector unsigned char kCharAlpha_Lower = (vector unsigned char)( 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1, 'a' - 1 ); 447 const vector unsigned char kCharZed_Lower = (vector unsigned char)( 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1, 'z' + 1 ); 448 const vector unsigned char kCharZero = (vector unsigned char)( '0' - 1, '0' - 1, '0' - 1, '0' - 1, '0' - 1, '0' - 1, '0' - 1, '0' - 1, '0' - 1, '0' - 1, '0' - 1, '0' - 1, '0' - 1, '0' - 1, '0' - 1, '0' - 1 ); 449 const vector unsigned char kCharNine = (vector unsigned char)( '9' + 1, '9' + 1, '9' + 1, '9' + 1, '9' + 1, '9' + 1, '9' + 1, '9' + 1, '9' + 1, '9' + 1, '9' + 1, '9' + 1, '9' + 1, '9' + 1, '9' + 1, '9' + 1 ); 450 const vector unsigned char kCharPlus = (vector unsigned char)( '+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+' ); 451 const vector unsigned char kCharSlash = (vector unsigned char)( '/', '/', '/', '/', '/', '/', '/', '/', '/', '/', '/', '/', '/', '/', '/', '/' ); 452 const vector unsigned char kCharEqual = (vector unsigned char)( '=', '=', '=', '=', '=', '=', '=', '=', '=', '=', '=', '=', '=', '=', '=', '=' ); 453 const vector unsigned char kTwentySix = (vector unsigned char)( 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26 ); 454 const vector unsigned char kFiftyTwo = (vector unsigned char)( 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52 ); 455 const vector unsigned char kSixtyTwo = (vector unsigned char)( 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62 ); 456 const vector unsigned char kSixtyThree = (vector unsigned char)( 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 ); 457 vector unsigned char kTwoFiftyFive; 458 459 // Create a couple of vectors we want to use. 460 kOne = vec_splat_u8( 1 ); 461 kTwoFiftyFive = vec_add( vec_nor( kOne, kOne ), kOne ); 462 while ( inboundData < finishedPtr ) 463 { 464 if ( finishedPtr - inboundData >= 16 ) 465 { 466 // Copy the next sixteen characters into the working vector. 467 ((unsigned long *)&workingVec)[ 0 ] = *(unsigned long *)inboundData; 468 ((unsigned long *)&workingVec)[ 1 ] = *(unsigned long *)&inboundData[ 4 ]; 469 ((unsigned long *)&workingVec)[ 2 ] = *(unsigned long *)&inboundData[ 8 ]; 470 ((unsigned long *)&workingVec)[ 3 ] = *(unsigned long *)&inboundData[ 12 ]; 471 inboundData += 16; 472 } 473 else 474 { 475 // Copy valid characters in and fill the rest with zero. 476 for ( i = 0; i < 16; i++ ) 477 { 478 if ( inboundData < finishedPtr ) 479 ((unsigned char *)&workingVec)[ i ] = *(inboundData++); 480 else 481 ((unsigned char *)&workingVec)[ i ] = 0; 482 } // end for loop 483 } 484 485 // Look for capital letters, lower case letters, numbers, plus signs, slashes, and equal signs. 486 foundCaps = vec_and( vec_cmpgt( workingVec, kCharAlpha_Cap ), vec_cmplt( workingVec, kCharZed_Cap ) ); 487 foundLower = vec_and( vec_cmpgt( workingVec, kCharAlpha_Lower ), vec_cmplt( workingVec, kCharZed_Lower ) ); 488 foundNum = vec_and( vec_cmpgt( workingVec, kCharZero ), vec_cmplt( workingVec, kCharNine ) ); 489 foundPlus = vec_cmpeq( workingVec, kCharPlus ); 490 foundSlash = vec_cmpeq( workingVec, kCharSlash ); 491 foundEqual = vec_cmpeq( workingVec, kCharEqual ); 492 493 // Adjust Base64 symbols into indices 0 to 63. 494 // Select the parts of the data we want. 495 // Note that in this first one, we're getting garbage where we didn't have caps. 496 finishedVec = vec_sub( workingVec, vec_add( kCharAlpha_Cap, kOne ) ); 497 finishedVec = vec_sel( finishedVec, vec_add( vec_sub( workingVec, vec_add( kCharAlpha_Lower, kOne ) ), kTwentySix ), foundLower ); 498 finishedVec = vec_sel( finishedVec, vec_add( vec_sub( workingVec, vec_add( kCharZero, kOne ) ), kFiftyTwo ), foundNum ); 499 finishedVec = vec_sel( finishedVec, kSixtyTwo, foundPlus ); 500 finishedVec = vec_sel( finishedVec, kSixtyThree, foundSlash ); 501 502 // Stick in a 255 for any equal signs (255 is our stop signal). 503 finishedVec = vec_sel( finishedVec, kTwoFiftyFive, foundEqual ); 504 505 // Combine all foundBlah comparisons so we can grab only real Base64 data. 506 foundCaps = vec_or( vec_or( vec_or( foundCaps, foundLower ), vec_or( foundNum, foundPlus ) ), 507 vec_or( foundSlash, foundEqual ) ); 508 509 for ( i = 0; i < 16; i++ ) 510 { 511 if ( ((unsigned char *)&foundCaps)[ i ] != 0 ) 512 // Do not ignore this byte. 513 *outboundData++ = ((unsigned char *)&finishedVec)[ i ]; 514 } // end for loop copying valid data to outBoundData 515 516 // If we found any equal signs on this block, we're done. 517 if ( vec_any_eq( workingVec, kCharEqual ) ) 518 { 519 foundAnEqual = YES; 520 break; 521 } 522 } // end for loop over all incoming data. 523 524 if ( foundAnEqual && 255 == *( outboundData - 2 ) ) 525 { 526 // Check to see if we hit two equal signs at the end of the data. 527 // If so, back up the outboundData pointer by one so we only include the first stop signal. 528 outboundData--; 529 } 530 531 // How much valid data did we end up with? 532 return outboundData - outData; 533} 534 535static unsigned long local_altiVec_decode( unsigned char *inBytes, unsigned long inBytesLength, unsigned char *outData, unsigned long *outDecodedLength ) 536{ 537 unsigned char *finishedPtr; 538 unsigned long result = ( inBytesLength / 16 ) * 16; // round down to nearest multiple of 16 539 unsigned long *outboundData = (unsigned long *)outData; 540 vector unsigned char shiftedRight, outputVec; 541 vector unsigned char kZero = { 0 }; 542 const vector unsigned char kShiftLeft = (vector unsigned char)( 2, 4, 6, 0, 2, 4, 6, 0, 2, 4, 6, 0, 2, 4, 6, 0 ); 543 const vector unsigned char kShiftRight = (vector unsigned char)( 0, 4, 2, 0, 0, 4, 2, 0, 0, 4, 2, 0, 0, 4, 2, 0 ); 544 const vector unsigned char kPermuteShiftedRight = (vector unsigned char)( 1, 2, 3, 16, 5, 6, 7, 16, 9, 10, 11, 16, 13, 14, 15, 16 ); 545 const vector unsigned char kPermuteRelevantData = (vector unsigned char)( 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 16, 16, 16, 16 ); 546 547 // Determine when we will stop looping. 548 // We only do multiples of sixteen inbound bytes at a time. 549 // Any extra data will need to be decoded from Base64 using the non-AltiVec algorithm. 550 finishedPtr = inBytes + result; 551 552 while ( inBytes < finishedPtr ) 553 { 554 outputVec = *(vector unsigned char *)inBytes; 555 inBytes += 16; 556 557 // Do the shift right first, so the vec_perm can be working at the same time as the next vec_sl. 558 shiftedRight = vec_perm( vec_sr( outputVec, kShiftRight ), kZero, kPermuteShiftedRight ); 559 560 // Some bits need shifting to the left. 561 // Combine the shifted left and shifted right bits. 562 outputVec = vec_or( shiftedRight, vec_sl( outputVec, kShiftLeft ) ); 563 564 // Mash the relevant bytes together. 565 outputVec = vec_perm( outputVec, kZero, kPermuteRelevantData ); 566 567 // Grab the relevant twelve bytes from the vector. 568 // Note that to save some CPU cycles we copy three longs (at four bytes each). 569 *(outboundData++) = ((unsigned long *)&outputVec)[ 0 ]; 570 *(outboundData++) = ((unsigned long *)&outputVec)[ 1 ]; 571 *(outboundData++) = ((unsigned long *)&outputVec)[ 2 ]; 572 } // end while loop 573 574 // Specify how much data we created. 575 *outDecodedLength = (unsigned char *)outboundData - outData; 576 577 // Return how much of the incoming data we finished off. 578 return result; 579} 580 581#endif // end compiling for AltiVec 582 583static unsigned long local_preprocessForDecode( const unsigned char *inBytes, unsigned long inBytesLength, unsigned char *outData ) 584{ 585 unsigned long i; 586 unsigned char *outboundData = outData; 587 unsigned char ch; 588 589 for ( i = 0; i < inBytesLength; i++ ) 590 { 591 ch = inBytes[ i ]; 592 593 if ((ch >= 'A') && (ch <= 'Z')) 594 *outboundData++ = ch - 'A'; 595 596 else if ((ch >= 'a') && (ch <= 'z')) 597 *outboundData++ = ch - 'a' + 26; 598 599 else if ((ch >= '0') && (ch <= '9')) 600 *outboundData++ = ch - '0' + 52; 601 602 else if (ch == '+') 603 *outboundData++ = 62; 604 605 else if (ch == '/') 606 *outboundData++ = 63; 607 608 else if (ch == '=') 609 { // no op -- put in our stop signal 610 *outboundData++ = 255; 611 break; 612 } 613 } 614 615 // How much valid data did we end up with? 616 return outboundData - outData; 617} 618