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