PageRenderTime 87ms CodeModel.GetById 14ms app.highlight 70ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 19#import "GTMStringEncoding.h"
 20
 21enum {
 22  kUnknownChar = -1,
 23  kPaddingChar = -2,
 24  kIgnoreChar = -3
 25};
 26
 27@implementation GTMStringEncoding
 28
 29+ (id)binaryStringEncoding {
 30  return [self stringEncodingWithString:@"01"];
 31}
 32
 33+ (id)hexStringEncoding {
 34  GTMStringEncoding *ret = [self stringEncodingWithString:
 35      @"0123456789ABCDEF"];
 36  [ret addDecodeSynonyms:@"AaBbCcDdEeFf"];
 37  return ret;
 38}
 39
 40+ (id)rfc4648Base32StringEncoding {
 41  GTMStringEncoding *ret = [self stringEncodingWithString:
 42      @"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"];
 43  [ret setPaddingChar:'='];
 44  [ret setDoPad:YES];
 45  return ret;
 46}
 47
 48+ (id)rfc4648Base32HexStringEncoding {
 49  GTMStringEncoding *ret = [self stringEncodingWithString:
 50      @"0123456789ABCDEFGHIJKLMNOPQRSTUV"];
 51  [ret setPaddingChar:'='];
 52  [ret setDoPad:YES];
 53  return ret;
 54}
 55
 56+ (id)crockfordBase32StringEncoding {
 57  GTMStringEncoding *ret = [self stringEncodingWithString:
 58      @"0123456789ABCDEFGHJKMNPQRSTVWXYZ"];
 59  [ret addDecodeSynonyms:
 60      @"0oO1iIlLAaBbCcDdEeFfGgHhJjKkMmNnPpQqRrSsTtVvWwXxYyZz"];
 61  return ret;
 62}
 63
 64+ (id)rfc4648Base64StringEncoding {
 65  GTMStringEncoding *ret = [self stringEncodingWithString:
 66      @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"];
 67  [ret setPaddingChar:'='];
 68  [ret setDoPad:YES];
 69  return ret;
 70}
 71
 72+ (id)rfc4648Base64WebsafeStringEncoding {
 73  GTMStringEncoding *ret = [self stringEncodingWithString:
 74      @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"];
 75  [ret setPaddingChar:'='];
 76  [ret setDoPad:YES];
 77  return ret;
 78}
 79
 80GTM_INLINE int lcm(int a, int b) {
 81  for (int aa = a, bb = b;;) {
 82    if (aa == bb)
 83      return aa;
 84    else if (aa < bb)
 85      aa += a;
 86    else
 87      bb += b;
 88  }
 89}
 90
 91+ (id)stringEncodingWithString:(NSString *)string {
 92  return [[[self alloc] initWithString:string] autorelease];
 93}
 94
 95- (id)initWithString:(NSString *)string {
 96  if ((self = [super init])) {
 97    charMapData_ = [[string dataUsingEncoding:NSASCIIStringEncoding] retain];
 98    if (!charMapData_) {
 99      _GTMDevLog(@"Unable to convert string to ASCII");
100      [self release];
101      return nil;
102    }
103    charMap_ = (char *)[charMapData_ bytes];
104    NSUInteger length = [charMapData_ length];
105    if (length < 2 || length > 128 || length & (length - 1)) {
106      _GTMDevLog(@"Length not a power of 2 between 2 and 128");
107      [self release];
108      return nil;
109    }
110
111    memset(reverseCharMap_, kUnknownChar, sizeof(reverseCharMap_));
112    for (unsigned int i = 0; i < length; i++) {
113      if (reverseCharMap_[(int)charMap_[i]] != kUnknownChar) {
114        _GTMDevLog(@"Duplicate character at pos %d", i);
115        [self release];
116        return nil;
117      }
118      reverseCharMap_[(int)charMap_[i]] = i;
119    }
120
121    for (NSUInteger i = 1; i < length; i <<= 1)
122      shift_++;
123    mask_ = (1 << shift_) - 1;
124    padLen_ = lcm(8, shift_) / shift_;
125  }
126  return self;
127}
128
129- (void)dealloc {
130  [charMapData_ release];
131  [super dealloc];
132}
133
134- (NSString *)description {
135  // TODO(iwade) track synonyms
136  return [NSString stringWithFormat:@"<Base%d StringEncoder: %@>",
137          1 << shift_, charMapData_];
138}
139
140- (void)addDecodeSynonyms:(NSString *)synonyms {
141  char *buf = (char *)[synonyms cStringUsingEncoding:NSASCIIStringEncoding];
142  int val = kUnknownChar;
143  while (*buf) {
144    int c = *buf++;
145    if (reverseCharMap_[c] == kUnknownChar) {
146      reverseCharMap_[c] = val;
147    } else {
148      val = reverseCharMap_[c];
149    }
150  }
151}
152
153- (void)ignoreCharacters:(NSString *)chars {
154  char *buf = (char *)[chars cStringUsingEncoding:NSASCIIStringEncoding];
155  while (*buf) {
156    int c = *buf++;
157    _GTMDevAssert(reverseCharMap_[c] == kUnknownChar,
158                  @"Character already mapped");
159    reverseCharMap_[c] = kIgnoreChar;
160  }
161}
162
163- (BOOL)doPad {
164  return doPad_;
165}
166
167- (void)setDoPad:(BOOL)doPad {
168  doPad_ = doPad;
169}
170
171- (void)setPaddingChar:(char)c {
172  paddingChar_ = c;
173  reverseCharMap_[(int)c] = kPaddingChar;
174}
175
176- (NSString *)encode:(NSData *)inData {
177  NSUInteger inLen = [inData length];
178  if (inLen <= 0) {
179    _GTMDevLog(@"Empty input");
180    return @"";
181  }
182  unsigned char *inBuf = (unsigned char *)[inData bytes];
183  NSUInteger inPos = 0;
184
185  NSUInteger outLen = (inLen * 8 + shift_ - 1) / shift_;
186  if (doPad_) {
187    outLen = ((outLen + padLen_ - 1) / padLen_) * padLen_;
188  }
189  NSMutableData *outData = [NSMutableData dataWithLength:outLen];
190  unsigned char *outBuf = (unsigned char *)[outData mutableBytes];
191  NSUInteger outPos = 0;
192
193  int buffer = inBuf[inPos++];
194  int bitsLeft = 8;
195  while (bitsLeft > 0 || inPos < inLen) {
196    if (bitsLeft < shift_) {
197      if (inPos < inLen) {
198        buffer <<= 8;
199        buffer |= (inBuf[inPos++] & 0xff);
200        bitsLeft += 8;
201      } else {
202        int pad = shift_ - bitsLeft;
203        buffer <<= pad;
204        bitsLeft += pad;
205      }
206    }
207    int idx = (buffer >> (bitsLeft - shift_)) & mask_;
208    bitsLeft -= shift_;
209    outBuf[outPos++] = charMap_[idx];
210  }
211
212  if (doPad_) {
213    while (outPos < outLen)
214      outBuf[outPos++] = paddingChar_;
215  }
216
217  _GTMDevAssert(outPos == outLen, @"Underflowed output buffer");
218  [outData setLength:outPos];
219
220  return [[[NSString alloc] initWithData:outData
221                                encoding:NSASCIIStringEncoding] autorelease];
222}
223
224- (NSString *)encodeString:(NSString *)inString {
225  return [self encode:[inString dataUsingEncoding:NSUTF8StringEncoding]];
226}
227
228- (NSData *)decode:(NSString *)inString {
229  char *inBuf = (char *)[inString cStringUsingEncoding:NSASCIIStringEncoding];
230  if (!inBuf) {
231    _GTMDevLog(@"unable to convert buffer to ASCII");
232    return nil;
233  }
234  NSUInteger inLen = strlen(inBuf);
235
236  NSUInteger outLen = inLen * shift_ / 8;
237  NSMutableData *outData = [NSMutableData dataWithLength:outLen];
238  unsigned char *outBuf = (unsigned char *)[outData mutableBytes];
239  NSUInteger outPos = 0;
240
241  int buffer = 0;
242  int bitsLeft = 0;
243  BOOL expectPad = NO;
244  for (NSUInteger i = 0; i < inLen; i++) {
245    int val = reverseCharMap_[(int)inBuf[i]];
246    switch (val) {
247      case kIgnoreChar:
248        break;
249      case kPaddingChar:
250        expectPad = YES;
251        break;
252      case kUnknownChar:
253        _GTMDevLog(@"Unexpected data in input pos %lu", (unsigned long)i);
254        return nil;
255      default:
256        if (expectPad) {
257          _GTMDevLog(@"Expected further padding characters");
258          return nil;
259        }
260        buffer <<= shift_;
261        buffer |= val & mask_;
262        bitsLeft += shift_;
263        if (bitsLeft >= 8) {
264          outBuf[outPos++] = (unsigned char)(buffer >> (bitsLeft - 8));
265          bitsLeft -= 8;
266        }
267        break;
268    }
269  }
270
271  if (bitsLeft && buffer & ((1 << bitsLeft) - 1)) {
272    _GTMDevLog(@"Incomplete trailing data");
273    return nil;
274  }
275
276  // Shorten buffer if needed due to padding chars
277  _GTMDevAssert(outPos <= outLen, @"Overflowed buffer");
278  [outData setLength:outPos];
279
280  return outData;
281}
282
283- (NSString *)stringByDecoding:(NSString *)inString {
284  NSData *ret = [self decode:inString];
285  return [[[NSString alloc] initWithData:ret
286                                encoding:NSUTF8StringEncoding] autorelease];
287}
288
289@end