Merge pull request #9 from gunyarakun/fix-typo
[cocotron.git] / Foundation / NSUnarchiver.m
blob960f8f9d5f6bbdbb0bffe0db17682344c3ef4570
1 /* Copyright (c) 2006-2007 Christopher J. W. Lloyd
3 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
8 #import <Foundation/NSUnarchiver.h>
9 #import <Foundation/NSRaise.h>
10 #import <Foundation/NSData.h>
11 #import <Foundation/NSMutableArray.h>
12 #import <Foundation/NSByteOrder.h>
13 #include <string.h>
15 @implementation NSUnarchiver
17 -(void)cannotDecodeType:(const char *)type {
18    [NSException raise:@"NSUnarchiverCannotDecodeException"
19                format:@"NSUnarchiver cannot decode type=%s",type];
22 -(void)_ensureLength:(NSUInteger)length {
23    if(_position+length>_length)
24     [NSException raise:@"NSUnarchiverBadArchiveException"
25                 format:@"NSUnarchiver attempt to read beyond length"];
28 -(uint8_t)_extractWordOne {
29    [self _ensureLength:1];
30    return _bytes[_position++];
33 -(uint16_t)_extractWordTwo {
34    uint16_t result;
36    [self _ensureLength:2];
38    result=_bytes[_position++];
39    result<<=8;
40    result|=_bytes[_position++];
42    return result;
45 -(unsigned int)_extractWordFour {
46    unsigned int result;
48    [self _ensureLength:4];
50    result=_bytes[_position++];
51    result<<=8;
52    result|=_bytes[_position++];
53    result<<=8;
54    result|=_bytes[_position++];
55    result<<=8;
56    result|=_bytes[_position++];
58    return result;
61 -(unsigned long long)_extractWordEight {
62    unsigned long long result;
64    [self _ensureLength:8];
66    result=_bytes[_position++];
67    result<<=8;
68    result|=_bytes[_position++];
69    result<<=8;
70    result|=_bytes[_position++];
71    result<<=8;
72    result|=_bytes[_position++];
73    result<<=8;
74    result|=_bytes[_position++];
75    result<<=8;
76    result|=_bytes[_position++];
77    result<<=8;
78    result|=_bytes[_position++];
79    result<<=8;
80    result|=_bytes[_position++];
82    return result;
85 -(float)_extractDataFloat {
86    NSSwappedFloat swapped;
88    swapped.floatWord=[self _extractWordFour];
90    return NSConvertSwappedFloatToHost(swapped);
93 -(double)_extractDataDouble {
94    NSSwappedDouble swapped;
96    swapped.doubleWord=[self _extractWordEight];
98    return NSConvertSwappedDoubleToHost(swapped);
101 -(NSString *)_extractCStringBytes {
102    NSString *result;
103    NSUInteger  length=0;
105    while((_position+length)<_length && (_bytes[_position+length]!='\0'))
106     length++;
108    result=[NSString stringWithCString:(char *)(_bytes+_position) length:length];
109    _position+=length+1;
111    return result;
114 -(NSUInteger)_extractReference {
115    return (NSUInteger)[self _extractWordFour];
118 -(NSString *)_extractCStringString {
119    NSUInteger  ref=[self _extractReference];
120    NSString *result=NSMapGet(_cStrings,(void *)ref);
122    if(result==nil){
123     result=[self _extractCStringBytes];
124     NSMapInsert(_cStrings,(void *)ref,result);
125    }
127    return result;
130 -(const char *)_extractCString {
131    return [[self _extractCStringString] cString];
134 -(Class)_extractClass {
135    NSUInteger ref=[self _extractReference];
136    Class    result;
138    if(ref==0)
139     return [NSObject class];
140    else if((result=NSMapGet(_classes,(void *)ref))!=Nil)
141     return result;
142    else {
143     NSString *className=[self _extractCStringString];
144     NSUInteger  version=[self _extractWordFour];
146     result=NSClassFromString(className);
148     NSMapInsert(_classes,(void *)ref,result);
149     NSMapInsert(_classVersions,className,(void *)version);
151     [self _extractClass];
153     return result;
154    }
157 -(id)_extractObject {
158    NSUInteger  ref=[self _extractReference];
159    id        result;
161    if(ref==0)
162     return nil;
163    else if((result=NSMapGet(_objects,(void *)ref))!=nil)
164     [result retain];
165    else{
166     Class class=[self _extractClass];
168     result=[class allocWithZone:NULL];
169     NSMapInsert(_objects,(void *)ref,result);
170     result=[result initWithCoder:self];
171     result=[result awakeAfterUsingCoder:self];
173     NSMapInsert(_objects,(void *)ref,result);
175     [_allObjects addObject:result];
176    }
178    return result;
181 -(void)_extractArrayOfObjCType:(const char *)type length:(NSUInteger)length
182   at:(void *)addr {
184    switch(*type){
185     case 'c':
186     case 'C':{
187       unsigned char *values=addr;
188       int i;
190       for(i=0;i<length;i++)
191        values[i]=[self _extractWordOne];
192      }
193      break;
195     case 's':
196     case 'S':{
197       unsigned short *values=addr;
198       int i;
200       for(i=0;i<length;i++)
201        values[i]=[self _extractWordTwo];
202      }
203      break;
205     default:
206      [self cannotDecodeType:type];
207      break;
208    }
211 -(void)decodeValueOfObjCType:(const char *)type at:(void *)addr {
212    const char *checkType=[self _extractCString];
214    if(strcmp(checkType,type)!=0)
215     [NSException raise:@"NSUnarchiverTypeMismatchException"
216                 format:@"NSUnarchiver type mismatch decoding %s, contains %s",type,checkType];
218    switch(*type){
219     case 'c':
220     case 'C':{
221       unsigned char *value=addr;
222       *value=[self _extractWordOne];
223      }
224      break;
226     case 's':
227     case 'S':{
228       unsigned short *value=addr;
229       *value=[self _extractWordTwo];
230      }
231      break;
233     case 'i':
234     case 'I':{
235       unsigned int *value=addr;
236       *value=[self _extractWordFour];
237      }
238      break;
240     case 'l':
241     case 'L':{
242       unsigned long *value=addr;
243       *value=[self _extractWordFour];
244      }
245      break;
247     case 'q':
248     case 'Q':{
249       unsigned long long *value=addr;
250       *value=[self _extractWordEight];
251      }
252      break;
254     case 'f':{
255       float *value=addr;
256       *value=[self _extractDataFloat];
257      }
258      break;
260     case 'd':{
261       double *value=addr;
262       *value=[self _extractDataDouble];
263      }
264      break;
266     case '*':{
267       char     **cString=addr;
268       NSString *string=[self _extractCStringString];
270       *cString=NSZoneMalloc(NSDefaultMallocZone(),[string cStringLength]+1);
271       [string getCString:*cString];
272      }
273      break;
275     case '@':{
276       id *object=addr;
278       *object=[self _extractObject];
279      }
280      break;
282     case '#':
283      [self cannotDecodeType:type];
284      break;
286     case ':':{
287       SEL      *value=addr;
288       NSString *string=[self _extractCStringString];
290       *value=NSSelectorFromString(string);
291      }
292      break;
294     case '[':{
295       const char *tmp=type;
296       NSUInteger    length=0;
298       tmp++; // skip [
299       for(;*tmp>='0' && *tmp<='9';tmp++)
300        length=(length*10)+(*tmp-'0');
302       [self _extractArrayOfObjCType:tmp length:length at:addr];
303      }
304      break;
306     case '{':
307      if(strcmp(type,"{?=II}")==0){
308       NSRange *value=addr;
310       value->location=[self _extractWordFour];
311       value->length=[self _extractWordFour];
312       break;
313      }
314      if(strcmp(type,@encode(NSPoint))==0){
315       NSPoint *value=addr;
317       value->x=[self _extractDataFloat];
318       value->y=[self _extractDataFloat];
319       break;
320      }
321      if(strcmp(type,@encode(NSSize))==0){
322       NSSize *value=addr;
324       value->width=[self _extractDataFloat];
325       value->height=[self _extractDataFloat];
326       break;
327      }
328      if(strcmp(type,@encode(NSRect))==0){
329       NSRect *value=addr;
331       value->origin.x=[self _extractDataFloat];
332       value->origin.y=[self _extractDataFloat];
333       value->size.width=[self _extractDataFloat];
334       value->size.height=[self _extractDataFloat];
335       break;
336      }
337      [self cannotDecodeType:type];
338      break;
340     case '(':
341     case 'b':
342     case '^':
343     case '?':
344     default:
345      [self cannotDecodeType:type];
346      break;
347    }
351 -(void *)decodeBytesWithReturnedLength:(NSUInteger *)lengthp {
352    void    *result;
353    unsigned length=[self _extractWordFour];
355    [self _ensureLength:length];
356    result=(void *)(_bytes+_position);
357    _position+=length;
359   *lengthp=length;
361    return result;
365 -(NSData *)decodeDataObject {
366    [self cannotDecodeType:"decodeDataObject"];
367    return nil;
371 - (NSInteger)versionForClassName:(NSString *)className
373     void *oKey, *oVal;
375     if (!NSMapMember(_classVersions, className, &oKey, &oVal)) {
376         //NSLog(@"no version for %@",className);
377     }
379     return (NSInteger)NSMapGet(_classVersions, className);
383 -(BOOL)invalidHeader {
384    NSString *label;
386    if(_length<strlen("~V1~")+1+4)
387     return YES;
389    label=[self _extractCStringBytes];
390    if(![label isEqualToString:@"~V1~"])
391     return YES;
393    _version=[self _extractWordFour];
394    if(_version>0)
395     [NSException raise:@"NSUnarchiverInvalidVersionException"
396                 format:@"NSUnarchiver cannot unarchive version %d",_version];
398    return NO;
401 -initForReadingWithData:(NSData *)data {
402    _data=[data copy];
403    _bytes=[data bytes];
404    _position=0;
405    _length=[data length];
407    _objectZone=NSDefaultMallocZone();
408    _version=0;
409    _objects=NSCreateMapTable(NSIntMapKeyCallBacks,
410        NSNonRetainedObjectMapValueCallBacks,0);
411    _classes=NSCreateMapTable(NSIntMapKeyCallBacks,
412       NSNonRetainedObjectMapValueCallBacks,0);
413    _cStrings=NSCreateMapTable(NSIntMapKeyCallBacks,NSObjectMapValueCallBacks,0);
414    _classVersions=NSCreateMapTable(NSObjectMapKeyCallBacks,
415         NSIntMapValueCallBacks,0);
417    _allObjects=[NSMutableArray new];
419    if([self invalidHeader]){
420     [self dealloc];
421     return nil;
422    }
423    return self;
426 -(void)dealloc {
427    [_data release];
428    NSFreeMapTable(_objects);
429    NSFreeMapTable(_classes);
430    NSFreeMapTable(_cStrings);
431    NSFreeMapTable(_classVersions);
432    [_allObjects release];
433    [super dealloc];
436 +(id)unarchiveObjectWithData:(NSData *)data {
437    NSUnarchiver *unarchiver=[[[NSUnarchiver allocWithZone:NULL] initForReadingWithData:data] autorelease];
439    return [unarchiver decodeObject];
442 +(id)unarchiveObjectWithFile:(NSString *)path {
443    NSData       *data=[NSData dataWithContentsOfFile:path];
444    NSUnarchiver *unarchiver;
446    if(data==nil)
447     return nil;
449    unarchiver=[[[NSUnarchiver allocWithZone:NULL] initForReadingWithData:data] autorelease];
451    return [unarchiver decodeObject];
454 -(BOOL)isAtEnd {
455    return (_position<_length)?NO:YES;
458 -(NSZone *)objectZone {
459    return _objectZone;
462 -(void)setObjectZone:(NSZone *)zone {
463    _objectZone=zone;
466 -(void)decodeClassName:(NSString *)archiveName asClassName:(NSString *)runtimeName {
467    NSUnimplementedMethod();
470 -(NSString *)classNameDecodedForArchiveClassName:(NSString *)className {
471    NSUnimplementedMethod();
472    return 0;
475 +(void)decodeClassName:(NSString *)archiveName asClassName:(NSString *)runtimeName {
476    NSUnimplementedMethod();
479 +(NSString *)classNameDecodedForArchiveClassName:(NSString *)className {
480    NSUnimplementedMethod();
481    return 0;
484 -(void)replaceObject:original withObject:replacement {
485    NSUnimplementedMethod();
488 @end