/core/externals/google-toolbox-for-mac/Foundation/GTMStringEncoding.m

http://macfuse.googlecode.com/ · Objective C · 289 lines · 233 code · 37 blank · 19 comment · 37 complexity · 232bbee66c2e2a02123fe9e763954092 MD5 · raw file

  1. //
  2. // GTMStringEncoding.m
  3. //
  4. // Copyright 2009 Google Inc.
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7. // use this file except in compliance with the License. You may obtain a copy
  8. // of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  15. // License for the specific language governing permissions and limitations under
  16. // the License.
  17. //
  18. #import "GTMStringEncoding.h"
  19. enum {
  20. kUnknownChar = -1,
  21. kPaddingChar = -2,
  22. kIgnoreChar = -3
  23. };
  24. @implementation GTMStringEncoding
  25. + (id)binaryStringEncoding {
  26. return [self stringEncodingWithString:@"01"];
  27. }
  28. + (id)hexStringEncoding {
  29. GTMStringEncoding *ret = [self stringEncodingWithString:
  30. @"0123456789ABCDEF"];
  31. [ret addDecodeSynonyms:@"AaBbCcDdEeFf"];
  32. return ret;
  33. }
  34. + (id)rfc4648Base32StringEncoding {
  35. GTMStringEncoding *ret = [self stringEncodingWithString:
  36. @"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"];
  37. [ret setPaddingChar:'='];
  38. [ret setDoPad:YES];
  39. return ret;
  40. }
  41. + (id)rfc4648Base32HexStringEncoding {
  42. GTMStringEncoding *ret = [self stringEncodingWithString:
  43. @"0123456789ABCDEFGHIJKLMNOPQRSTUV"];
  44. [ret setPaddingChar:'='];
  45. [ret setDoPad:YES];
  46. return ret;
  47. }
  48. + (id)crockfordBase32StringEncoding {
  49. GTMStringEncoding *ret = [self stringEncodingWithString:
  50. @"0123456789ABCDEFGHJKMNPQRSTVWXYZ"];
  51. [ret addDecodeSynonyms:
  52. @"0oO1iIlLAaBbCcDdEeFfGgHhJjKkMmNnPpQqRrSsTtVvWwXxYyZz"];
  53. return ret;
  54. }
  55. + (id)rfc4648Base64StringEncoding {
  56. GTMStringEncoding *ret = [self stringEncodingWithString:
  57. @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"];
  58. [ret setPaddingChar:'='];
  59. [ret setDoPad:YES];
  60. return ret;
  61. }
  62. + (id)rfc4648Base64WebsafeStringEncoding {
  63. GTMStringEncoding *ret = [self stringEncodingWithString:
  64. @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"];
  65. [ret setPaddingChar:'='];
  66. [ret setDoPad:YES];
  67. return ret;
  68. }
  69. GTM_INLINE int lcm(int a, int b) {
  70. for (int aa = a, bb = b;;) {
  71. if (aa == bb)
  72. return aa;
  73. else if (aa < bb)
  74. aa += a;
  75. else
  76. bb += b;
  77. }
  78. }
  79. + (id)stringEncodingWithString:(NSString *)string {
  80. return [[[self alloc] initWithString:string] autorelease];
  81. }
  82. - (id)initWithString:(NSString *)string {
  83. if ((self = [super init])) {
  84. charMapData_ = [[string dataUsingEncoding:NSASCIIStringEncoding] retain];
  85. if (!charMapData_) {
  86. _GTMDevLog(@"Unable to convert string to ASCII");
  87. [self release];
  88. return nil;
  89. }
  90. charMap_ = (char *)[charMapData_ bytes];
  91. NSUInteger length = [charMapData_ length];
  92. if (length < 2 || length > 128 || length & (length - 1)) {
  93. _GTMDevLog(@"Length not a power of 2 between 2 and 128");
  94. [self release];
  95. return nil;
  96. }
  97. memset(reverseCharMap_, kUnknownChar, sizeof(reverseCharMap_));
  98. for (unsigned int i = 0; i < length; i++) {
  99. if (reverseCharMap_[(int)charMap_[i]] != kUnknownChar) {
  100. _GTMDevLog(@"Duplicate character at pos %d", i);
  101. [self release];
  102. return nil;
  103. }
  104. reverseCharMap_[(int)charMap_[i]] = i;
  105. }
  106. for (NSUInteger i = 1; i < length; i <<= 1)
  107. shift_++;
  108. mask_ = (1 << shift_) - 1;
  109. padLen_ = lcm(8, shift_) / shift_;
  110. }
  111. return self;
  112. }
  113. - (void)dealloc {
  114. [charMapData_ release];
  115. [super dealloc];
  116. }
  117. - (NSString *)description {
  118. // TODO(iwade) track synonyms
  119. return [NSString stringWithFormat:@"<Base%d StringEncoder: %@>",
  120. 1 << shift_, charMapData_];
  121. }
  122. - (void)addDecodeSynonyms:(NSString *)synonyms {
  123. char *buf = (char *)[synonyms cStringUsingEncoding:NSASCIIStringEncoding];
  124. int val = kUnknownChar;
  125. while (*buf) {
  126. int c = *buf++;
  127. if (reverseCharMap_[c] == kUnknownChar) {
  128. reverseCharMap_[c] = val;
  129. } else {
  130. val = reverseCharMap_[c];
  131. }
  132. }
  133. }
  134. - (void)ignoreCharacters:(NSString *)chars {
  135. char *buf = (char *)[chars cStringUsingEncoding:NSASCIIStringEncoding];
  136. while (*buf) {
  137. int c = *buf++;
  138. _GTMDevAssert(reverseCharMap_[c] == kUnknownChar,
  139. @"Character already mapped");
  140. reverseCharMap_[c] = kIgnoreChar;
  141. }
  142. }
  143. - (BOOL)doPad {
  144. return doPad_;
  145. }
  146. - (void)setDoPad:(BOOL)doPad {
  147. doPad_ = doPad;
  148. }
  149. - (void)setPaddingChar:(char)c {
  150. paddingChar_ = c;
  151. reverseCharMap_[(int)c] = kPaddingChar;
  152. }
  153. - (NSString *)encode:(NSData *)inData {
  154. NSUInteger inLen = [inData length];
  155. if (inLen <= 0) {
  156. _GTMDevLog(@"Empty input");
  157. return @"";
  158. }
  159. unsigned char *inBuf = (unsigned char *)[inData bytes];
  160. NSUInteger inPos = 0;
  161. NSUInteger outLen = (inLen * 8 + shift_ - 1) / shift_;
  162. if (doPad_) {
  163. outLen = ((outLen + padLen_ - 1) / padLen_) * padLen_;
  164. }
  165. NSMutableData *outData = [NSMutableData dataWithLength:outLen];
  166. unsigned char *outBuf = (unsigned char *)[outData mutableBytes];
  167. NSUInteger outPos = 0;
  168. int buffer = inBuf[inPos++];
  169. int bitsLeft = 8;
  170. while (bitsLeft > 0 || inPos < inLen) {
  171. if (bitsLeft < shift_) {
  172. if (inPos < inLen) {
  173. buffer <<= 8;
  174. buffer |= (inBuf[inPos++] & 0xff);
  175. bitsLeft += 8;
  176. } else {
  177. int pad = shift_ - bitsLeft;
  178. buffer <<= pad;
  179. bitsLeft += pad;
  180. }
  181. }
  182. int idx = (buffer >> (bitsLeft - shift_)) & mask_;
  183. bitsLeft -= shift_;
  184. outBuf[outPos++] = charMap_[idx];
  185. }
  186. if (doPad_) {
  187. while (outPos < outLen)
  188. outBuf[outPos++] = paddingChar_;
  189. }
  190. _GTMDevAssert(outPos == outLen, @"Underflowed output buffer");
  191. [outData setLength:outPos];
  192. return [[[NSString alloc] initWithData:outData
  193. encoding:NSASCIIStringEncoding] autorelease];
  194. }
  195. - (NSString *)encodeString:(NSString *)inString {
  196. return [self encode:[inString dataUsingEncoding:NSUTF8StringEncoding]];
  197. }
  198. - (NSData *)decode:(NSString *)inString {
  199. char *inBuf = (char *)[inString cStringUsingEncoding:NSASCIIStringEncoding];
  200. if (!inBuf) {
  201. _GTMDevLog(@"unable to convert buffer to ASCII");
  202. return nil;
  203. }
  204. NSUInteger inLen = strlen(inBuf);
  205. NSUInteger outLen = inLen * shift_ / 8;
  206. NSMutableData *outData = [NSMutableData dataWithLength:outLen];
  207. unsigned char *outBuf = (unsigned char *)[outData mutableBytes];
  208. NSUInteger outPos = 0;
  209. int buffer = 0;
  210. int bitsLeft = 0;
  211. BOOL expectPad = NO;
  212. for (NSUInteger i = 0; i < inLen; i++) {
  213. int val = reverseCharMap_[(int)inBuf[i]];
  214. switch (val) {
  215. case kIgnoreChar:
  216. break;
  217. case kPaddingChar:
  218. expectPad = YES;
  219. break;
  220. case kUnknownChar:
  221. _GTMDevLog(@"Unexpected data in input pos %lu", (unsigned long)i);
  222. return nil;
  223. default:
  224. if (expectPad) {
  225. _GTMDevLog(@"Expected further padding characters");
  226. return nil;
  227. }
  228. buffer <<= shift_;
  229. buffer |= val & mask_;
  230. bitsLeft += shift_;
  231. if (bitsLeft >= 8) {
  232. outBuf[outPos++] = (unsigned char)(buffer >> (bitsLeft - 8));
  233. bitsLeft -= 8;
  234. }
  235. break;
  236. }
  237. }
  238. if (bitsLeft && buffer & ((1 << bitsLeft) - 1)) {
  239. _GTMDevLog(@"Incomplete trailing data");
  240. return nil;
  241. }
  242. // Shorten buffer if needed due to padding chars
  243. _GTMDevAssert(outPos <= outLen, @"Overflowed buffer");
  244. [outData setLength:outPos];
  245. return outData;
  246. }
  247. - (NSString *)stringByDecoding:(NSString *)inString {
  248. NSData *ret = [self decode:inString];
  249. return [[[NSString alloc] initWithData:ret
  250. encoding:NSUTF8StringEncoding] autorelease];
  251. }
  252. @end