Base64.m 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. //
  2. // Base64.m
  3. //
  4. // Version 1.1
  5. //
  6. // Created by Nick Lockwood on 12/01/2012.
  7. // Copyright (C) 2012 Charcoal Design
  8. //
  9. // Distributed under the permissive zlib License
  10. // Get the latest version from here:
  11. //
  12. // https://github.com/nicklockwood/Base64
  13. //
  14. // This software is provided 'as-is', without any express or implied
  15. // warranty. In no event will the authors be held liable for any damages
  16. // arising from the use of this software.
  17. //
  18. // Permission is granted to anyone to use this software for any purpose,
  19. // including commercial applications, and to alter it and redistribute it
  20. // freely, subject to the following restrictions:
  21. //
  22. // 1. The origin of this software must not be misrepresented; you must not
  23. // claim that you wrote the original software. If you use this software
  24. // in a product, an acknowledgment in the product documentation would be
  25. // appreciated but is not required.
  26. //
  27. // 2. Altered source versions must be plainly marked as such, and must not be
  28. // misrepresented as being the original software.
  29. //
  30. // 3. This notice may not be removed or altered from any source distribution.
  31. //
  32. #import "Base64.h"
  33. #import <Availability.h>
  34. #if !__has_feature(objc_arc)
  35. #error This library requires automatic reference counting
  36. #endif
  37. @implementation NSData (Base64)
  38. + (NSData *)dataWithBase64EncodedString:(NSString *)string
  39. {
  40. const char lookup[] =
  41. {
  42. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  43. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  44. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 62, 99, 99, 99, 63,
  45. 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 99, 99, 99, 99, 99, 99,
  46. 99, 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, 99, 99, 99, 99, 99,
  48. 99, 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, 99, 99, 99, 99, 99
  50. };
  51. NSData *inputData = [string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
  52. long long inputLength = [inputData length];
  53. const unsigned char *inputBytes = [inputData bytes];
  54. long long maxOutputLength = (inputLength / 4 + 1) * 3;
  55. NSMutableData *outputData = [NSMutableData dataWithLength:maxOutputLength];
  56. unsigned char *outputBytes = (unsigned char *)[outputData mutableBytes];
  57. int accumulator = 0;
  58. long long outputLength = 0;
  59. unsigned char accumulated[] = {0, 0, 0, 0};
  60. for (long long i = 0; i < inputLength; i++)
  61. {
  62. unsigned char decoded = lookup[inputBytes[i] & 0x7F];
  63. if (decoded != 99)
  64. {
  65. accumulated[accumulator] = decoded;
  66. if (accumulator == 3)
  67. {
  68. outputBytes[outputLength++] = (accumulated[0] << 2) | (accumulated[1] >> 4);
  69. outputBytes[outputLength++] = (accumulated[1] << 4) | (accumulated[2] >> 2);
  70. outputBytes[outputLength++] = (accumulated[2] << 6) | accumulated[3];
  71. }
  72. accumulator = (accumulator + 1) % 4;
  73. }
  74. }
  75. //handle left-over data
  76. if (accumulator > 0) outputBytes[outputLength] = (accumulated[0] << 2) | (accumulated[1] >> 4);
  77. if (accumulator > 1) outputBytes[++outputLength] = (accumulated[1] << 4) | (accumulated[2] >> 2);
  78. if (accumulator > 2) outputLength++;
  79. //truncate data to match actual output length
  80. outputData.length = outputLength;
  81. return outputLength? outputData: nil;
  82. }
  83. - (NSString *)base64EncodedStringWithWrapWidth:(NSUInteger)wrapWidth
  84. {
  85. //ensure wrapWidth is a multiple of 4
  86. wrapWidth = (wrapWidth / 4) * 4;
  87. const char lookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  88. long long inputLength = [self length];
  89. const unsigned char *inputBytes = [self bytes];
  90. long long maxOutputLength = (inputLength / 3 + 1) * 4;
  91. maxOutputLength += wrapWidth? (maxOutputLength / wrapWidth) * 2: 0;
  92. unsigned char *outputBytes = (unsigned char *)malloc(maxOutputLength);
  93. long long i;
  94. long long outputLength = 0;
  95. for (i = 0; i < inputLength - 2; i += 3)
  96. {
  97. outputBytes[outputLength++] = lookup[(inputBytes[i] & 0xFC) >> 2];
  98. outputBytes[outputLength++] = lookup[((inputBytes[i] & 0x03) << 4) | ((inputBytes[i + 1] & 0xF0) >> 4)];
  99. outputBytes[outputLength++] = lookup[((inputBytes[i + 1] & 0x0F) << 2) | ((inputBytes[i + 2] & 0xC0) >> 6)];
  100. outputBytes[outputLength++] = lookup[inputBytes[i + 2] & 0x3F];
  101. //add line break
  102. if (wrapWidth && (outputLength + 2) % (wrapWidth + 2) == 0)
  103. {
  104. outputBytes[outputLength++] = '\r';
  105. outputBytes[outputLength++] = '\n';
  106. }
  107. }
  108. //handle left-over data
  109. if (i == inputLength - 2)
  110. {
  111. // = terminator
  112. outputBytes[outputLength++] = lookup[(inputBytes[i] & 0xFC) >> 2];
  113. outputBytes[outputLength++] = lookup[((inputBytes[i] & 0x03) << 4) | ((inputBytes[i + 1] & 0xF0) >> 4)];
  114. outputBytes[outputLength++] = lookup[(inputBytes[i + 1] & 0x0F) << 2];
  115. outputBytes[outputLength++] = '=';
  116. }
  117. else if (i == inputLength - 1)
  118. {
  119. // == terminator
  120. outputBytes[outputLength++] = lookup[(inputBytes[i] & 0xFC) >> 2];
  121. outputBytes[outputLength++] = lookup[(inputBytes[i] & 0x03) << 4];
  122. outputBytes[outputLength++] = '=';
  123. outputBytes[outputLength++] = '=';
  124. }
  125. if (outputLength >= 4)
  126. {
  127. //truncate data to match actual output length
  128. outputBytes = realloc(outputBytes, outputLength);
  129. return [[NSString alloc] initWithBytesNoCopy:outputBytes
  130. length:outputLength
  131. encoding:NSASCIIStringEncoding
  132. freeWhenDone:YES];
  133. }
  134. else if (outputBytes)
  135. {
  136. free(outputBytes);
  137. }
  138. return nil;
  139. }
  140. - (NSString *)base64EncodedString
  141. {
  142. return [self base64EncodedStringWithWrapWidth:0];
  143. }
  144. @end
  145. @implementation NSString (Base64)
  146. + (NSString *)stringWithBase64EncodedString:(NSString *)string
  147. {
  148. NSData *data = [NSData dataWithBase64EncodedString:string];
  149. if (data)
  150. {
  151. return [[self alloc] initWithData:data encoding:NSUTF8StringEncoding];
  152. }
  153. return nil;
  154. }
  155. - (NSString *)base64EncodedStringWithWrapWidth:(NSUInteger)wrapWidth
  156. {
  157. NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
  158. return [data base64EncodedStringWithWrapWidth:wrapWidth];
  159. }
  160. - (NSString *)base64EncodedString
  161. {
  162. NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
  163. return [data base64EncodedString];
  164. }
  165. - (NSString *)base64DecodedString
  166. {
  167. return [NSString stringWithBase64EncodedString:self];
  168. }
  169. - (NSData *)base64DecodedData
  170. {
  171. return [NSData dataWithBase64EncodedString:self];
  172. }
  173. @end