1/** Implementation of NSUnarchiver for GNUstep
2   Copyright (C) 1998 Free Software Foundation, Inc.
3
4   Written by:  Richard Frith-Macdonald <richard@brainstorm.co.uk>
5   Created: October 1998
6
7   This file is part of the GNUstep Base Library.
8
9   This library is free software; you can redistribute it and/or
10   modify it under the terms of the GNU Lesser General Public
11   License as published by the Free Software Foundation; either
12   version 2 of the License, or (at your option) any later version.
13
14   This library is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   Lesser General Public License for more details.
18
19   You should have received a copy of the GNU Lesser General Public
20   License along with this library; if not, write to the Free
21   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22   Boston, MA 02110 USA.
23
24   <title>NSUnarchiver class reference</title>
25   $Date$ $Revision$
26   */
27
28#import "common.h"
29
30#if !defined (__GNU_LIBOBJC__)
31#  include <objc/encoding.h>
32#endif
33
34#define	EXPOSE_NSUnarchiver_IVARS	1
35#import "Foundation/NSDictionary.h"
36#import "Foundation/NSException.h"
37#import "Foundation/NSByteOrder.h"
38
39/*
40 *	Setup for inline operation of arrays.
41 */
42#define	GSI_ARRAY_NO_RETAIN	1
43#define	GSI_ARRAY_NO_RELEASE	1
44#define	GSI_ARRAY_TYPES	GSUNION_OBJ|GSUNION_SEL|GSUNION_PTR
45
46#include "GNUstepBase/GSIArray.h"
47
48#define	_IN_NSUNARCHIVER_M
49#import "Foundation/NSArchiver.h"
50#undef	_IN_NSUNARCHIVER_M
51
52#import "Foundation/NSAutoreleasePool.h"
53#import "Foundation/NSCoder.h"
54#import "Foundation/NSData.h"
55#import "Foundation/NSArray.h"
56
57@class NSDataMalloc;
58@interface NSDataMalloc : NSObject	// Help the compiler
59@end
60
61static const char*
62typeToName1(char type)
63{
64  switch (type)
65    {
66      case _C_CLASS:	return "class";
67      case _C_ID:	return "object";
68      case _C_SEL:	return "selector";
69      case _C_CHR:	return "char";
70      case _C_UCHR:	return "unsigned char";
71      case _C_SHT:	return "short";
72      case _C_USHT:	return "unsigned short";
73      case _C_INT:	return "int";
74      case _C_UINT:	return "unsigned int";
75      case _C_LNG:	return "long";
76      case _C_ULNG:	return "unsigned long";
77      case _C_LNG_LNG:	return "long long";
78      case _C_ULNG_LNG:	return "unsigned long long";
79      case _C_FLT:	return "float";
80      case _C_DBL:	return "double";
81#if __GNUC__ > 2 && defined(_C_BOOL)
82      case _C_BOOL:	return "_Bool";
83#endif
84      case _C_PTR:	return "pointer";
85      case _C_CHARPTR:	return "cstring";
86      case _C_ARY_B:	return "array";
87      case _C_STRUCT_B:	return "struct";
88      default:
89	{
90	  static char	buf1[32];
91	  static char	buf2[32];
92	  static char	*bufptr = buf1;
93
94	  if (bufptr == buf1)
95	    {
96	      bufptr = buf2;
97	    }
98	  else
99	    {
100	      bufptr = buf1;
101	    }
102	  snprintf(bufptr, 32, "unknown type info - 0x%x", type);
103	  return bufptr;
104	}
105    }
106}
107
108static const char*
109typeToName2(char type)
110{
111  switch (type & _GSC_MASK)
112    {
113      case _GSC_CID:	return "class (encoded as id)";
114      case _GSC_CLASS:	return "class";
115      case _GSC_ID:	return "object";
116      case _GSC_SEL:	return "selector";
117      case _GSC_CHR:	return "char";
118      case _GSC_UCHR:	return "unsigned char";
119      case _GSC_SHT:	return "short";
120      case _GSC_USHT:	return "unsigned short";
121      case _GSC_INT:	return "int";
122      case _GSC_UINT:	return "unsigned int";
123      case _GSC_LNG:	return "long";
124      case _GSC_ULNG:	return "unsigned long";
125      case _GSC_LNG_LNG:	return "long long";
126      case _GSC_ULNG_LNG:	return "unsigned long long";
127      case _GSC_FLT:	return "float";
128      case _GSC_DBL:	return "double";
129      case _GSC_BOOL:	return "_Bool";
130      case _GSC_PTR:	return "pointer";
131      case _GSC_CHARPTR:	return "cstring";
132      case _GSC_ARY_B:	return "array";
133      case _GSC_STRUCT_B:	return "struct";
134      default:
135	{
136	  static char	buf1[32];
137	  static char	buf2[32];
138	  static char	*bufptr = buf1;
139
140	  if (bufptr == buf1)
141	    {
142	      bufptr = buf2;
143	    }
144	  else
145	    {
146	      bufptr = buf1;
147	    }
148	  snprintf(bufptr, 32, "unknown type info - 0x%x", type);
149	  return bufptr;
150	}
151    }
152}
153
154/*
155 *	There are thirtyone possible basic types.  We reserve a type of zero
156 *	to mean that no information is specified.  The slots in this array
157 *	MUST correspond to the definitions in NSData.h
158 */
159static char	type_map[32] = {
160  0,
161  _C_CHR,
162  _C_UCHR,
163  _C_SHT,
164  _C_USHT,
165  _C_INT,
166  _C_UINT,
167  _C_LNG,
168  _C_ULNG,
169  _C_LNG_LNG,
170  _C_ULNG_LNG,
171  _C_FLT,
172  _C_DBL,
173#if __GNUC__ > 2 && defined(_C_BOOL)
174  _C_BOOL,
175#else
176  0,
177#endif
178  0,
179  0,
180  _C_ID,
181  _C_CLASS,
182  _C_SEL,
183  _C_PTR,
184  _C_CHARPTR,
185  _C_ARY_B,
186  _C_STRUCT_B,
187  0,
188  0,
189  0,
190  0,
191  0,
192  0,
193  0,
194  0,
195  0
196};
197
198
199static inline BOOL
200typeCheck(char t1, char t2)
201{
202  if (type_map[(t2 & _GSC_MASK)] != t1)
203    {
204/*
205 * HACK ... allow int/long/longlong types to be used interchangably
206 * as the ObjC compiler currently uses quadword (q/Q) encoding for
207 * integer types on some 64bit systems, so the i/l/q/I/L/Q encodings
208 * can vary.
209 */
210      char	c = type_map[(t2 & _GSC_MASK)];
211      char      s1;
212      char      s2;
213
214      switch (t1)
215        {
216          case _C_SHT:  s1 = _GSC_S_SHT; break;
217          case _C_USHT:  s1 = _GSC_S_SHT; break;
218          case _C_INT:  s1 = _GSC_S_INT; break;
219          case _C_UINT:  s1 = _GSC_S_INT; break;
220          case _C_LNG:  s1 = _GSC_S_LNG; break;
221          case _C_ULNG:  s1 = _GSC_S_LNG; break;
222          case _C_LNG_LNG:  s1 = _GSC_S_LNG_LNG; break;
223          case _C_ULNG_LNG:  s1 = _GSC_S_LNG_LNG; break;
224          default:      s1 = 0;
225        }
226
227      switch (t2)
228        {
229          case _C_SHT:  s2 = _GSC_S_SHT; break;
230          case _C_USHT:  s2 = _GSC_S_SHT; break;
231          case _C_INT:  s2 = _GSC_S_INT; break;
232          case _C_UINT:  s2 = _GSC_S_INT; break;
233          case _C_LNG:  s2 = _GSC_S_LNG; break;
234          case _C_ULNG:  s2 = _GSC_S_LNG; break;
235          case _C_LNG_LNG:  s2 = _GSC_S_LNG_LNG; break;
236          case _C_ULNG_LNG:  s2 = _GSC_S_LNG_LNG; break;
237          default:      s2 = 0;
238        }
239
240      if ((c == _C_INT || c == _C_LNG || c == _C_LNG_LNG)
241        && (t1 == _C_INT || t1 == _C_LNG || t1 == _C_LNG_LNG))
242        return s1 == s2 ? YES : NO;
243
244      if ((c == _C_UINT || c == _C_ULNG || c == _C_ULNG_LNG)
245        && (t1 == _C_UINT || t1 == _C_ULNG || t1 == _C_ULNG_LNG))
246        return s1 == s2 ? YES : NO;
247
248/* HACK also allow float and double to be used interchangably as MacOS-X
249 * intorduced CGFloat, which may be aither a float or a double.
250 */
251      if ((c == _C_FLT || c == _C_DBL) && (t1 == _C_FLT || t1 == _C_DBL))
252	return NO;
253
254      [NSException raise: NSInternalInconsistencyException
255		  format: @"expected %s and got %s",
256		    typeToName1(t1), typeToName2(t2)];
257    }
258  return YES;
259}
260
261#define	PREFIX		"GNUstep archive"
262
263static SEL desSel;
264static SEL tagSel;
265static SEL dValSel;
266
267@interface	NSUnarchiverClassInfo : NSObject
268{
269@public
270  NSString	*original;
271  NSString	*name;
272  Class		class;
273}
274+ (id) newWithName: (NSString*)n;
275- (void) mapToClass: (Class)c withName: (NSString*)name;
276@end
277
278@implementation	NSUnarchiverClassInfo
279+ (id) newWithName: (NSString*)n
280{
281  NSUnarchiverClassInfo	*info;
282
283  info = (NSUnarchiverClassInfo*)NSAllocateObject(self,0,NSDefaultMallocZone());
284  if (info)
285    {
286      info->original = [n copyWithZone: NSDefaultMallocZone()];
287    }
288  return info;
289}
290- (void) dealloc
291{
292  RELEASE(original);
293  if (name)
294    {
295      RELEASE(name);
296    }
297  NSDeallocateObject(self);
298  GSNOSUPERDEALLOC;
299}
300- (void) mapToClass: (Class)c withName: (NSString*)n
301{
302  ASSIGN(name, n);
303  class = c;
304}
305@end
306
307/*
308 *	Dictionary used by NSUnarchiver class to keep track of
309 *	NSUnarchiverClassInfo objects used to map classes by name when
310 *	unarchiving.
311 */
312static NSMutableDictionary	*clsDict;	/* Class information	*/
313
314@interface	NSUnarchiverObjectInfo : NSUnarchiverClassInfo
315{
316@public
317  NSInteger	version;
318  NSUnarchiverClassInfo	*overrides;
319}
320@end
321
322static inline Class
323mapClassObject(NSUnarchiverObjectInfo *info)
324{
325  if (info->overrides == nil)
326    {
327      info->overrides = [clsDict objectForKey: info->original];
328    }
329  if (info->overrides)
330    {
331      return info->overrides->class;
332    }
333  else
334    {
335      return info->class;
336    }
337}
338
339static inline NSString*
340mapClassName(NSUnarchiverObjectInfo *info)
341{
342  if (info->overrides == nil)
343    {
344      info->overrides = [clsDict objectForKey: info->original];
345    }
346  if (info->overrides)
347    {
348      return info->overrides->name;
349    }
350  else
351   {
352      return info->name;
353   }
354}
355
356@implementation	NSUnarchiverObjectInfo
357@end
358
359/**
360 * <p>This class reconstructs objects from an archive.</p><br />
361 * <strong>Re-using the archiver</strong>
362 * <p>
363 *   The -resetUnarchiverWithData:atIndex: method lets you re-use
364 *   the archive to decode a new data object or, in conjunction
365 *   with the 'cursor' method (which reports the current decoding
366 *   position in the archive), decode a second archive that exists
367 *   in the data object after the first one.
368 * </p>
369 * <strong>Subclassing with different input format.</strong><br /><br />
370 * <code>NSUnarchiver</code> normally reads directly from an [NSData]
371 * object using the methods -
372 * <deflist>
373 *   <term>-deserializeTypeTag:andCrossRef:atCursor:</term>
374 *   <desc>
375 *     to decode type tags for data items, the tag is the
376 *     first byte of the character encoding string for the
377 *     data type (as provided by '@encode(xxx)'), possibly
378 *     with the top bit set to indicate that what follows is
379 *     a crossreference to an item already encoded.<br />
380 *     Also decode a crossreference number either to identify the
381 *     following item, or to refer to a previously encoded item.
382 *     Objects, Classes, Selectors, CStrings and Pointer items
383 *     have crossreference encoding, other types do not.<br />
384 *   </desc>
385 *   <term>[NSData-deserializeDataAt:ofObjCType:atCursor:context:]</term>
386 *   <desc>
387 *     to decode all other information.
388 *   </desc>
389 * </deflist>
390 * <p>
391 *   <code>NSUnarchiver</code> normally uses other [NSData] methods to read
392 *   the archive header information from within the method:
393 *   [-deserializeHeaderAt:version:classes:objects:pointers:]
394 *   to read a fixed size header including archiver version
395 *   (obtained by <code>[self systemVersion]</code>) and crossreference
396 *   table sizes.
397 * </p>
398 * <p>
399 *   To subclass <code>NSUnarchiver</code>, you must implement your own
400 *   versions of the four methods above, and override the 'directDataAccess'
401 *   method to return NO so that the archiver knows to use your serialization
402 *   methods rather than those in the [NSData] object.
403 * </p>
404 */
405@implementation NSUnarchiver
406
407static Class NSDataMallocClass;
408static unsigned	encodingVersion;
409
410+ (void) initialize
411{
412  if ([self class] == [NSUnarchiver class])
413    {
414      NSArchiver	*archiver = [NSArchiver new];
415
416      encodingVersion = [archiver systemVersion];
417      [archiver release];
418      desSel = @selector(deserializeDataAt:ofObjCType:atCursor:context:);
419      tagSel = @selector(deserializeTypeTag:andCrossRef:atCursor:);
420      dValSel = @selector(decodeValueOfObjCType:at:);
421      clsDict = [[NSMutableDictionary alloc] initWithCapacity: 200];
422      NSDataMallocClass = [NSDataMalloc class];
423
424    }
425}
426
427/**
428 *  Creates an NSUnarchiver to read from anObject and returns result of sending
429 *  [NSCoder -decodeObject] to it.
430 */
431+ (id) unarchiveObjectWithData: (NSData*)anObject
432{
433  NSUnarchiver	*unarchiver;
434  id		obj;
435
436  unarchiver = [[self alloc] initForReadingWithData: anObject];
437  NS_DURING
438    {
439      obj = [unarchiver decodeObject];
440    }
441  NS_HANDLER
442    {
443      obj = nil;
444      RELEASE(unarchiver);
445      [localException raise];
446    }
447  NS_ENDHANDLER
448  RELEASE(unarchiver);
449
450  return obj;
451}
452
453/**
454 *  Creates an NSUnarchiver to read from path and returns result of sending
455 *  [NSCoder -decodeObject] to it.
456 */
457+ (id) unarchiveObjectWithFile: (NSString*)path
458{
459  NSData	*d = [NSDataMallocClass dataWithContentsOfFile: path];
460
461  if (d != nil)
462    {
463      return [self unarchiveObjectWithData: d];
464    }
465  return nil;
466}
467
468- (void) dealloc
469{
470  RELEASE(data);
471  RELEASE(objSave);
472  RELEASE(objDict);
473  if (clsMap)
474    {
475      NSZone	*z = clsMap->zone;
476
477      GSIArrayClear(clsMap);
478      GSIArrayClear(objMap);
479      GSIArrayClear(ptrMap);
480      NSZoneFree(z, (void*)clsMap);
481    }
482  [super dealloc];
483}
484
485/**
486 *  Set up to read objects from data buffer anObject.
487 */
488- (id) initForReadingWithData: (NSData*)anObject
489{
490  if (anObject == nil)
491    {
492      [NSException raise: NSInvalidArgumentException
493		  format: @"nil data passed to initForReadingWithData:"];
494    }
495
496  self = [super init];
497  if (self)
498    {
499      dValImp = [self methodForSelector: dValSel];
500      zone = [self zone];
501      /*
502       *	If we are not deserializing directly from the data object
503       *	then we cache our own deserialisation methods.
504       */
505      if ([self directDataAccess] == NO)
506	{
507	  src = self;		/* Default object to handle serialisation */
508	  desImp = [src methodForSelector: desSel];
509	  tagImp = (void (*)(id, SEL, unsigned char*, unsigned*, unsigned*))
510	      [src methodForSelector: tagSel];
511	}
512      /*
513       *	objDict is a dictionary of objects for mapping classes of
514       *	one name to be those of another name!  It also handles
515       *	keeping track of the version numbers that the classes were
516       *	encoded with.
517       */
518      objDict = [[NSMutableDictionary allocWithZone: zone]
519				   initWithCapacity: 200];
520      /*
521       *	objSave is an array used purely to ensure that objects
522       *	we decode persist until the end of the decoding.
523       */
524      objSave = [[NSMutableArray allocWithZone: zone]
525			      initWithCapacity: 200];
526
527      NS_DURING
528	{
529	  [self resetUnarchiverWithData: anObject atIndex: 0];
530	}
531      NS_HANDLER
532	{
533	  DESTROY(self);
534	  [localException raise];
535	}
536      NS_ENDHANDLER
537    }
538  return self;
539}
540
541- (void) decodeArrayOfObjCType: (const char*)type
542			 count: (NSUInteger)expected
543			    at: (void*)buf
544{
545  NSUInteger	i;
546  NSUInteger	offset = 0;
547  unsigned int	size = (unsigned int)objc_sizeof_type(type);
548  unsigned char	info;
549  unsigned char	ainfo;
550  unsigned char	amask;
551  NSUInteger	count;
552
553  (*tagImp)(src, tagSel, &info, 0, &cursor);
554  if ([self systemVersion] == 12402)
555    {
556      uint8_t	c;
557
558      /* Unpack variable length count.
559       */
560      count = 0;
561      for (;;)
562	{
563	  if (count * 128 < count)
564	    {
565	      [NSException raise: NSInternalInconsistencyException
566			  format: @"overflow in array count"];
567	    }
568	  count *= 128;
569	  (*desImp)(src, desSel, &c, @encode(uint8_t), &cursor, nil);
570	  if (c & 128)
571	    {
572	      count += (c & 127);
573	    }
574	  else
575	    {
576	      count += c;
577	      break;
578	    }
579	}
580    }
581  else
582    {
583      uint32_t	c;
584
585      (*desImp)(src, desSel, &c, @encode(uint32_t), &cursor, nil);
586      count = c;
587      if (0xffffffff == c)
588	{
589	  (*desImp)(src, desSel, &count, @encode(NSUInteger), &cursor, nil);
590	}
591    }
592  if (info != _GSC_ARY_B)
593    {
594      [NSException raise: NSInternalInconsistencyException
595		  format: @"expected array and got %s", typeToName2(info)];
596    }
597  if (count != expected)
598    {
599      [NSException raise: NSInternalInconsistencyException
600		  format: @"expected array count %"PRIuPTR" and got %"PRIuPTR"",
601			expected, count];
602    }
603
604  switch (*type)
605    {
606      case _C_ID:	info = _GSC_NONE; break;
607      case _C_CHR:	info = _GSC_CHR; break;
608      case _C_UCHR:	info = _GSC_UCHR; break;
609      case _C_SHT:	info = _GSC_SHT; break;
610      case _C_USHT:	info = _GSC_USHT; break;
611      case _C_INT:	info = _GSC_INT; break;
612      case _C_UINT:	info = _GSC_UINT; break;
613      case _C_LNG:	info = _GSC_LNG; break;
614      case _C_ULNG:	info = _GSC_ULNG; break;
615      case _C_LNG_LNG:	info = _GSC_LNG_LNG; break;
616      case _C_ULNG_LNG:	info = _GSC_ULNG_LNG; break;
617      case _C_FLT:	info = _GSC_FLT; break;
618      case _C_DBL:	info = _GSC_DBL; break;
619#if __GNUC__ > 2 && defined(_C_BOOL)
620      case _C_BOOL:	info = _GSC_BOOL; break;
621#endif
622      default:		info = _GSC_NONE; break;
623    }
624
625  if (info == _GSC_NONE)
626    {
627      for (i = 0; i < count; i++)
628	{
629	  (*dValImp)(self, dValSel, type, (char*)buf + offset);
630	  offset += size;
631	}
632      return;
633    }
634
635  (*tagImp)(src, tagSel, &ainfo, 0, &cursor);
636  amask = (ainfo & _GSC_MASK);
637
638  /* If we have a perfect type match or we are coding a class as an ID,
639   * we can just decode the array simply.
640   */
641  if (info == amask)
642    {
643      for (i = 0; i < count; i++)
644	{
645	  (*desImp)(src, desSel, (char*)buf + offset, type, &cursor, nil);
646	  offset += size;
647	}
648      return;
649    }
650
651  /* This will raise an exception if the types don't match at all.
652   */
653  typeCheck(*type, ainfo);
654
655  /* We fall through to here only when we have to decode a value
656   * whose natural size on this system is not the same as on the
657   * machine on which the archive was created.
658   */
659
660  if (*type == _C_FLT)
661    {
662      for (i = 0; i < count; i++)
663	{
664	  double	d;
665
666	  (*desImp)(src, desSel, &d, @encode(double), &cursor, nil);
667	  *(float*)(buf + offset) = (float)d;
668	  offset += size;
669	}
670    }
671  else if (*type == _C_DBL)
672    {
673      for (i = 0; i < count; i++)
674	{
675	  float		f;
676
677	  (*desImp)(src, desSel, &f, @encode(float), &cursor, nil);
678	  *(double*)(buf + offset) = (double)f;
679	  offset += size;
680	}
681    }
682  else if (*type == _C_SHT
683    || *type == _C_INT
684    || *type == _C_LNG
685    || *type == _C_LNG_LNG)
686    {
687      int64_t	big;
688      int64_t	max;
689      int64_t	min;
690
691      switch (size)
692	{
693	  case 1:
694	    max = INT8_MAX;
695	    min = INT8_MIN;
696	    break;
697	  case 2:
698	    max = INT16_MAX;
699	    min = INT16_MAX;
700	    break;
701	  case 4:
702	    max = INT32_MAX;
703	    min = INT32_MIN;
704	    break;
705	  default:
706	    max = INT64_MAX;
707	    min = INT64_MIN;
708	}
709
710      for (i = 0; i < count; i++)
711	{
712	  void	*address = (void*)buf + offset;
713
714	  switch (ainfo & _GSC_SIZE)
715	    {
716	      case _GSC_I16:	/* Encoded as 16-bit	*/
717		{
718		  int16_t	val;
719
720		  (*desImp)(src, desSel, &val, @encode(int16_t), &cursor, nil);
721		  big = val;
722		  break;
723		}
724
725	      case _GSC_I32:	/* Encoded as 32-bit	*/
726		{
727		  int32_t	val;
728
729		  (*desImp)(src, desSel, &val, @encode(int32_t), &cursor, nil);
730		  big = val;
731		  break;
732		}
733
734	      case _GSC_I64:	/* Encoded as 64-bit	*/
735		{
736		  (*desImp)(src, desSel, &big, @encode(int64_t), &cursor, nil);
737		  break;
738		}
739
740              default:
741                [NSException raise: NSGenericException
742                            format: @"invalid size in archive"];
743	    }
744	  /*
745	   * Now we copy from the big value to the destination location.
746	   */
747	  switch (size)
748	    {
749	      case 1:
750		*(int8_t*)address = (int8_t)big;
751		break;
752	      case 2:
753		*(int16_t*)address = (int16_t)big;
754		break;
755	      case 4:
756		*(int32_t*)address = (int32_t)big;
757		break;
758	      default:
759		*(int64_t*)address = big;
760	    }
761	  if (big < min || big > max)
762	    {
763	      NSLog(@"Lost information converting large decoded value");
764	    }
765	  offset += size;
766	}
767    }
768  else
769    {
770      uint64_t  big;
771      uint64_t	max;
772
773      switch (size)
774	{
775	  case 1:
776	    max = UINT8_MAX;
777	    break;
778	  case 2:
779	    max = UINT16_MAX;
780	    break;
781	  case 4:
782	    max = UINT32_MAX;
783	    break;
784	  default:
785	    max = UINT64_MAX;
786	}
787
788      for (i = 0; i < count; i++)
789	{
790	  void	*address = (void*)buf + offset;
791
792	  switch (info & _GSC_SIZE)
793	    {
794	      case _GSC_I16:	/* Encoded as 16-bit	*/
795		{
796		  uint16_t	val;
797
798		  (*desImp)(src, desSel, &val, @encode(uint16_t), &cursor, nil);
799		  big = val;
800		  break;
801		}
802
803	      case _GSC_I32:	/* Encoded as 32-bit	*/
804		{
805		  uint32_t	val;
806
807		  (*desImp)(src, desSel, &val, @encode(uint32_t), &cursor, nil);
808		  big = val;
809		  break;
810		}
811
812	      case _GSC_I64:	/* Encoded as 64-bit	*/
813		{
814		  (*desImp)(src, desSel, &big, @encode(uint64_t), &cursor, nil);
815		  break;
816		}
817
818              default:
819                [NSException raise: NSGenericException
820                            format: @"invalid size in archive"];
821	    }
822	  /*
823	   * Now we copy from the big value to the destination location.
824	   */
825	  switch (size)
826	    {
827	      case 1:
828		*(uint8_t*)address = (uint8_t)big;
829		break;
830	      case 2:
831		*(uint16_t*)address = (uint16_t)big;
832		break;
833	      case 4:
834		*(uint32_t*)address = (uint32_t)big;
835		break;
836	      case 8:
837		*(uint64_t*)address = big;
838	    }
839	  if (big > max)
840	    {
841	      NSLog(@"Lost information converting large decoded value");
842	    }
843	  offset += size;
844	}
845    }
846}
847
848static inline int
849scalarSize(char type)
850{
851  switch (type)
852    {
853      case _C_SHT:
854      case _C_USHT:     return _GSC_S_SHT;
855      case _C_INT:
856      case _C_UINT:	return _GSC_S_INT;
857      case _C_LNG:
858      case _C_ULNG:	return _GSC_S_LNG;
859      case _C_LNG_LNG:
860      case _C_ULNG_LNG:	return _GSC_S_LNG_LNG;
861      default:
862        [NSException raise: NSInvalidArgumentException
863                    format: @"scalarSize() called with non-scalar type"];
864    }
865  return -1;
866}
867
868- (void) decodeValueOfObjCType: (const char*)type
869			    at: (void*)address
870{
871  unsigned	xref;
872  unsigned char	info;
873
874  (*tagImp)(src, tagSel, &info, &xref, &cursor);
875
876  switch (info & _GSC_MASK)
877    {
878      case _GSC_ID:
879	{
880	  id		obj;
881
882	  typeCheck(*type, _GSC_ID);
883	  /*
884	   *	Special case - a zero crossref value size is a nil pointer.
885	   */
886	  if ((info & _GSC_SIZE) == 0)
887	    {
888	      obj = nil;
889	    }
890	  else
891	    {
892	      if (info & _GSC_XREF)
893		{
894		  if (xref >= GSIArrayCount(objMap))
895		    {
896		      [NSException raise: NSInternalInconsistencyException
897				  format: @"object crossref missing - %d",
898					xref];
899		    }
900		  obj = GSIArrayItemAtIndex(objMap, xref).obj;
901		  /*
902		   *	If it's a cross-reference, we need to retain it in
903		   *	order to give the appearance that it's actually a
904		   *	new object.
905		   */
906		  IF_NO_GC(RETAIN(obj));
907		}
908	      else
909		{
910		  Class	c;
911		  id	rep;
912
913		  if (xref != GSIArrayCount(objMap))
914		    {
915		      [NSException raise: NSInternalInconsistencyException
916				  format: @"extra object crossref - %d",
917					xref];
918		    }
919		  (*dValImp)(self, dValSel, @encode(Class), &c);
920
921		  if (c == 0)
922		    {
923		      [NSException raise: NSInternalInconsistencyException
924				  format: @"decoded nil class"];
925		    }
926		  obj = [c allocWithZone: zone];
927		  GSIArrayAddItem(objMap, (GSIArrayItem)obj);
928
929		  rep = [obj initWithCoder: self];
930		  if (rep != obj)
931		    {
932		      obj = rep;
933		      GSIArraySetItemAtIndex(objMap, (GSIArrayItem)obj, xref);
934		    }
935
936		  rep = [obj awakeAfterUsingCoder: self];
937		  if (rep != obj)
938		    {
939		      obj = rep;
940		      GSIArraySetItemAtIndex(objMap, (GSIArrayItem)obj, xref);
941		    }
942		  /*
943		   * The objMap does not retain objects, so in order to
944		   * be sure that a decoded object is not deallocated by
945		   * anything before it is needed (because it is decoded
946		   * later as a cross reference) we store it in objSave.
947		   */
948		  if (obj != nil)
949		    {
950		      [objSave addObject: obj];
951		    }
952		}
953	    }
954	  *(id*)address = obj;
955	  return;
956	}
957
958      case _GSC_CLASS:
959	{
960	  Class		c;
961	  NSUnarchiverObjectInfo	*classInfo;
962	  Class		dummy;
963
964	  if (*type != _C_ID)
965	    {
966	      typeCheck(*type, _GSC_CLASS);
967	    }
968	  /*
969	   *	Special case - a zero crossref value size is a nil pointer.
970	   */
971	  if ((info & _GSC_SIZE) == 0)
972	    {
973	      *(SEL*)address = 0;
974	      return;
975	    }
976	  if (info & _GSC_XREF)
977	    {
978	      if (xref >= GSIArrayCount(clsMap))
979		{
980		  [NSException raise: NSInternalInconsistencyException
981			      format: @"class crossref missing - %d", xref];
982		}
983	      classInfo = (NSUnarchiverObjectInfo*)
984		GSIArrayItemAtIndex(clsMap, xref).obj;
985	      *(Class*)address = mapClassObject(classInfo);
986	      return;
987	    }
988	  while ((info & _GSC_MASK) == _GSC_CLASS)
989	    {
990	      unsigned	cver;
991	      NSString	*className;
992	      uint16_t	nameLength;
993
994	      if (xref != GSIArrayCount(clsMap))
995		{
996		  [NSException raise: NSInternalInconsistencyException
997				format: @"extra class crossref - %d", xref];
998		}
999
1000	      /*
1001	       * A class is encoded as a 16-bit length, a sequence of
1002	       * characters providing its name, then a version number.
1003	       */
1004	      (*desImp)(src, desSel, &nameLength, @encode(uint16_t),
1005		&cursor, nil);
1006	      if (nameLength == 0)
1007		{
1008		  className = nil;
1009		}
1010	      else
1011		{
1012		  char	name[nameLength+1];
1013
1014		  [src deserializeBytes: name
1015				 length: nameLength
1016			       atCursor: &cursor];
1017		  name[nameLength] = '\0';
1018		  className = [[NSString alloc] initWithUTF8String: name];
1019		}
1020	      (*desImp)(src, desSel, &cver, @encode(unsigned), &cursor, nil);
1021	      if (className == 0)
1022		{
1023		  NSLog(@"[%s %s] decoded nil class name",
1024		    class_getName([self class]), sel_getName(_cmd));
1025		  className = @"_NSUnarchiverUnknownClass";
1026		}
1027	      classInfo = [objDict objectForKey: className];
1028	      if (nil == classInfo)
1029		{
1030		  classInfo = [NSUnarchiverObjectInfo newWithName: className];
1031		  c = NSClassFromString(className);
1032		  /*
1033		   * Show a warning, if the class name that's being used to
1034		   * build the class causes NSClassFromString to return nil.
1035		   * This means that the class is unknown to the runtime.
1036		   */
1037		  if (c == nil)
1038		    {
1039		      NSLog(@"Unable to find class named '%@'", className);
1040		    }
1041		  [classInfo mapToClass: c withName: className];
1042		  [objDict setObject: classInfo forKey: className];
1043		  RELEASE(classInfo);
1044		}
1045	      RELEASE(className);
1046	      classInfo->version = (NSInteger)cver;
1047	      GSIArrayAddItem(clsMap, (GSIArrayItem)((id)classInfo));
1048	      *(Class*)address = mapClassObject(classInfo);
1049	      /*
1050	       *	Point the address to a dummy location and read the
1051	       *	next tag - if it is another class, loop to get it.
1052	       */
1053	      address = &dummy;
1054	      (*tagImp)(src, tagSel, &info, &xref, &cursor);
1055	    }
1056	  if (info != _GSC_NONE)
1057	    {
1058	      [NSException raise: NSInternalInconsistencyException
1059			  format: @"class list improperly terminated"];
1060	    }
1061	  return;
1062	}
1063
1064      case _GSC_SEL:
1065	{
1066	  SEL		sel;
1067
1068	  typeCheck(*type, _GSC_SEL);
1069	  /*
1070	   *	Special case - a zero crossref value size is a nil pointer.
1071	   */
1072	  if ((info & _GSC_SIZE) == 0)
1073	    {
1074	      *(SEL*)address = 0;
1075	      return;
1076	    }
1077	  if (info & _GSC_XREF)
1078	    {
1079	      if (xref >= GSIArrayCount(ptrMap))
1080		{
1081		  [NSException raise: NSInternalInconsistencyException
1082			      format: @"sel crossref missing - %d", xref];
1083		}
1084	      sel = GSIArrayItemAtIndex(ptrMap, xref).sel;
1085	    }
1086	  else
1087	    {
1088	      if (xref != GSIArrayCount(ptrMap))
1089		{
1090		  [NSException raise: NSInternalInconsistencyException
1091			      format: @"extra sel crossref - %d", xref];
1092		}
1093	      (*desImp)(src, desSel, &sel, @encode(SEL), &cursor, nil);
1094	      GSIArrayAddItem(ptrMap, (GSIArrayItem)sel);
1095	    }
1096	  *(SEL*)address = sel;
1097	  return;
1098	}
1099
1100      case _GSC_ARY_B:
1101	{
1102	  int	count;
1103
1104	  typeCheck(*type, _GSC_ARY_B);
1105	  count = atoi(++type);
1106	  while (isdigit(*type))
1107	    {
1108	      type++;
1109	    }
1110	  [self decodeArrayOfObjCType: type count: count at: address];
1111	  return;
1112	}
1113
1114      case _GSC_STRUCT_B:
1115	{
1116	  struct objc_struct_layout layout;
1117
1118	  typeCheck(*type, _GSC_STRUCT_B);
1119	  objc_layout_structure (type, &layout);
1120	  while (objc_layout_structure_next_member (&layout))
1121	    {
1122	      unsigned		offset;
1123	      unsigned		align;
1124	      const char	*ftype;
1125
1126	      objc_layout_structure_get_info (&layout, &offset, &align, &ftype);
1127
1128	      (*dValImp)(self, dValSel, ftype, (char*)address + offset);
1129	    }
1130	  return;
1131	}
1132
1133      case _GSC_PTR:
1134	{
1135	  typeCheck(*type, _GSC_PTR);
1136	  /*
1137	   *	Special case - a zero crossref value size is a nil pointer.
1138	   */
1139	  if ((info & _GSC_SIZE) == 0)
1140	    {
1141	      *(void**)address = 0;
1142	      return;
1143	    }
1144	  if (info & _GSC_XREF)
1145	    {
1146	      if (xref >= GSIArrayCount(ptrMap))
1147		{
1148		  [NSException raise: NSInternalInconsistencyException
1149			      format: @"ptr crossref missing - %d", xref];
1150		}
1151	      *(void**)address = GSIArrayItemAtIndex(ptrMap, xref).ptr;
1152	    }
1153	  else
1154	    {
1155	      unsigned	size;
1156
1157	      if (GSIArrayCount(ptrMap) != xref)
1158		{
1159		  [NSException raise: NSInternalInconsistencyException
1160			      format: @"extra ptr crossref - %d", xref];
1161		}
1162
1163	      /*
1164	       *	Allocate memory for object to be decoded into and
1165	       *	add it to the crossref map.
1166	       */
1167	      size = objc_sizeof_type(++type);
1168	      *(void**)address = GSAutoreleasedBuffer(size);
1169	      GSIArrayAddItem(ptrMap, (GSIArrayItem)*(void**)address);
1170
1171	      /*
1172	       *	Decode value and add memory to map for crossrefs.
1173	       */
1174	      (*dValImp)(self, dValSel, type, *(void**)address);
1175	    }
1176	  return;
1177	}
1178
1179      case _GSC_CHARPTR:
1180	{
1181	  typeCheck(*type, _GSC_CHARPTR);
1182	  /*
1183	   *	Special case - a zero crossref value size is a nil pointer.
1184	   */
1185	  if ((info & _GSC_SIZE) == 0)
1186	    {
1187	      *(char**)address = 0;
1188	      return;
1189	    }
1190	  if (info & _GSC_XREF)
1191	    {
1192	      if (xref >= GSIArrayCount(ptrMap))
1193		{
1194		  [NSException raise: NSInternalInconsistencyException
1195			      format: @"string crossref missing - %d", xref];
1196		}
1197	      *(char**)address = GSIArrayItemAtIndex(ptrMap, xref).str;
1198	    }
1199	  else
1200	    {
1201	      if (xref != GSIArrayCount(ptrMap))
1202		{
1203		  [NSException raise: NSInternalInconsistencyException
1204			      format: @"extra string crossref - %d", xref];
1205		}
1206	      (*desImp)(src, desSel, address, @encode(char*), &cursor, nil);
1207	      GSIArrayAddItem(ptrMap, (GSIArrayItem)*(void**)address);
1208	    }
1209	  return;
1210	}
1211
1212      case _GSC_CHR:
1213      case _GSC_UCHR:
1214	/* Encoding of chars is not consistant across platforms, so we
1215	   loosen the type checking a little */
1216	if (*type != type_map[_GSC_CHR] && *type != type_map[_GSC_UCHR])
1217	  {
1218	    [NSException raise: NSInternalInconsistencyException
1219		        format: @"expected %s and got %s",
1220		    typeToName1(*type), typeToName2(info)];
1221	  }
1222	(*desImp)(src, desSel, address, type, &cursor, nil);
1223	return;
1224
1225      case _GSC_SHT:
1226      case _GSC_USHT:
1227	if (YES == typeCheck(*type, info & _GSC_MASK)
1228	  && (info & _GSC_SIZE) == scalarSize(*type))
1229	  {
1230	    (*desImp)(src, desSel, address, type, &cursor, nil);
1231	    return;
1232	  }
1233	break;
1234
1235      case _GSC_INT:
1236      case _GSC_UINT:
1237	if (YES == typeCheck(*type, info & _GSC_MASK)
1238	  &&  (info & _GSC_SIZE) == scalarSize(*type))
1239	  {
1240	    (*desImp)(src, desSel, address, type, &cursor, nil);
1241	    return;
1242	  }
1243	break;
1244
1245      case _GSC_LNG:
1246      case _GSC_ULNG:
1247	if (YES == typeCheck(*type, info & _GSC_MASK)
1248	  && (info & _GSC_SIZE) == scalarSize(*type))
1249	  {
1250	    (*desImp)(src, desSel, address, type, &cursor, nil);
1251	    return;
1252	  }
1253	break;
1254
1255      case _GSC_LNG_LNG:
1256      case _GSC_ULNG_LNG:
1257	if (YES == typeCheck(*type, info & _GSC_MASK)
1258	  && (info & _GSC_SIZE) == scalarSize(*type))
1259	  {
1260	    (*desImp)(src, desSel, address, type, &cursor, nil);
1261	    return;
1262	  }
1263	break;
1264
1265      case _GSC_FLT:
1266	if (YES == typeCheck(*type, _GSC_FLT)
1267	  && *type == _C_FLT)
1268	  {
1269	    (*desImp)(src, desSel, address, type, &cursor, nil);
1270	  }
1271	else
1272	  {
1273	    float	val;
1274
1275	    /* We found a float when expecting a double ... handle it.
1276	     */
1277	    (*desImp)(src, desSel, &val, @encode(float), &cursor, nil);
1278	    *(double*)address = (double)val;
1279	  }
1280	return;
1281
1282      case _GSC_DBL:
1283	if (YES == typeCheck(*type, _GSC_DBL)
1284	  && *type == _C_DBL)
1285	  {
1286	    (*desImp)(src, desSel, address, type, &cursor, nil);
1287	  }
1288	else
1289	  {
1290	    double	val;
1291
1292	    /* We found a double when expecting a float ... handle it.
1293	     */
1294	    (*desImp)(src, desSel, &val, @encode(double), &cursor, nil);
1295	    *(float*)address = (float)val;
1296	  }
1297	return;
1298
1299      case _GSC_BOOL:
1300	if (*type != type_map[_GSC_BOOL])
1301	  {
1302	    [NSException raise: NSInternalInconsistencyException
1303		        format: @"expected %s and got %s",
1304		    typeToName1(*type), typeToName2(info)];
1305	  }
1306	(*desImp)(src, desSel, address, type, &cursor, nil);
1307	return;
1308
1309      default:
1310	[NSException raise: NSInternalInconsistencyException
1311		    format: @"read unknown type info - %d", info];
1312    }
1313
1314{
1315  uint8_t       size;
1316
1317  /*
1318   *	We fall through to here only when we have to decode a value
1319   *	whose natural size on this system is not the same as on the
1320   *	machine on which the archive was created.
1321   */
1322
1323  switch (*type)
1324    {
1325      case _C_SHT:
1326      case _C_USHT:  size = sizeof(short); break;
1327      case _C_INT:
1328      case _C_UINT:  size = sizeof(int); break;
1329      case _C_LNG:
1330      case _C_ULNG:  size = sizeof(long); break;
1331      case _C_LNG_LNG:
1332      case _C_ULNG_LNG:  size = sizeof(long long); break;
1333      default:      size = 1;
1334    }
1335
1336  /*
1337   *	First, we read the data and convert it to the largest size
1338   *	this system can support.
1339   */
1340  if (*type == _C_SHT
1341    || *type == _C_INT
1342    || *type == _C_LNG
1343    || *type == _C_LNG_LNG)
1344    {
1345      int64_t   big;
1346
1347      switch (info & _GSC_SIZE)
1348        {
1349          case _GSC_I16:	/* Encoded as 16-bit	*/
1350            {
1351              int16_t	val;
1352
1353              (*desImp)(src, desSel, &val, @encode(int16_t), &cursor, nil);
1354              big = val;
1355              break;
1356            }
1357
1358          case _GSC_I32:	/* Encoded as 32-bit	*/
1359            {
1360              int32_t	val;
1361
1362              (*desImp)(src, desSel, &val, @encode(int32_t), &cursor, nil);
1363              big = val;
1364              break;
1365            }
1366
1367          case _GSC_I64:	/* Encoded as 64-bit	*/
1368            {
1369              (*desImp)(src, desSel, &big, @encode(int64_t), &cursor, nil);
1370              break;
1371            }
1372
1373          default:		/* A 128-bit value	*/
1374            {
1375              big = 0;
1376              [NSException raise: NSInternalInconsistencyException
1377                          format: @"Archiving of 128bit integer not allowed"];
1378            }
1379        }
1380      /*
1381       *	Now we copy from the big value to the destination location.
1382       */
1383      switch (size)
1384        {
1385          case 1:
1386            *(int8_t*)address = (int8_t)big;
1387            if (big > 127 || big < -128)
1388              {
1389                NSLog(@"Lost information converting decoded value to int8_t");
1390              }
1391            return;
1392          case 2:
1393            *(int16_t*)address = (int16_t)big;
1394            if (big > 32767 || big < -32768)
1395              {
1396                NSLog(@"Lost information converting decoded value to int16_t");
1397              }
1398            return;
1399          case 4:
1400            *(int32_t*)address = (int32_t)big;
1401            if (big > 2147483647 || big < -2147483648)
1402              {
1403                NSLog(@"Lost information converting decoded value to int32_t");
1404              }
1405            return;
1406          case 8:
1407            *(int64_t*)address = big;
1408            return;
1409          default:
1410            [NSException raise: NSInternalInconsistencyException
1411                        format: @"type/size information error"];
1412        }
1413    }
1414  else
1415    {
1416      uint64_t  big;
1417
1418      switch (info & _GSC_SIZE)
1419        {
1420          case _GSC_I16:	/* Encoded as 16-bit	*/
1421            {
1422              uint16_t	val;
1423
1424              (*desImp)(src, desSel, &val, @encode(uint16_t), &cursor, nil);
1425              big = val;
1426              break;
1427            }
1428
1429          case _GSC_I32:	/* Encoded as 32-bit	*/
1430            {
1431              uint32_t	val;
1432
1433              (*desImp)(src, desSel, &val, @encode(uint32_t), &cursor, nil);
1434              big = val;
1435              break;
1436            }
1437
1438          case _GSC_I64:	/* Encoded as 64-bit	*/
1439            {
1440              (*desImp)(src, desSel, &big, @encode(uint64_t), &cursor, nil);
1441              break;
1442            }
1443
1444          default:		/* A 128-bit value	*/
1445            {
1446              big = 0;
1447              [NSException raise: NSInternalInconsistencyException
1448                          format: @"Archiving of 128bit integer not allowed"];
1449            }
1450        }
1451      /*
1452       * Now we copy from the big value to the destination location.
1453       */
1454      switch (size)
1455        {
1456          case 1:
1457            if (big & ~0xffLL)
1458              {
1459                NSLog(@"Lost information converting decoded value to uint8_t");
1460              }
1461            *(uint8_t*)address = (uint8_t)big;
1462            return;
1463          case 2:
1464            if (big & ~0xffffLL)
1465              {
1466                NSLog(@"Lost information converting decoded value to uint16_t");
1467              }
1468            *(uint16_t*)address = (uint16_t)big;
1469            return;
1470          case 4:
1471            if (big & ~0xffffffffLL)
1472              {
1473                NSLog(@"Lost information converting decoded value to uint32_t");
1474              }
1475            *(uint32_t*)address = (uint32_t)big;
1476            return;
1477          case 8:
1478            *(uint64_t*)address = big;
1479            return;
1480          default:
1481            [NSException raise: NSInternalInconsistencyException
1482                        format: @"type/size information error"];
1483        }
1484    }
1485}
1486}
1487
1488- (NSData*) decodeDataObject
1489{
1490  unsigned	l;
1491
1492  (*dValImp)(self, dValSel, @encode(unsigned int), &l);
1493  if (l)
1494    {
1495      unsigned char	c;
1496
1497      (*dValImp)(self, dValSel, @encode(unsigned char), &c);
1498      if (c == 0)
1499	{
1500	  void		*b;
1501	  NSData	*d;
1502
1503	  b = NSZoneMalloc(zone, l);
1504	  [self decodeArrayOfObjCType: @encode(unsigned char)
1505				count: l
1506				   at: b];
1507	  d = [[NSData allocWithZone: zone] initWithBytesNoCopy: b
1508							 length: l];
1509	  IF_NO_GC(AUTORELEASE(d));
1510	  return d;
1511	}
1512      else
1513	{
1514	  [NSException raise: NSInternalInconsistencyException
1515		      format: @"Decoding data object with unknown type"];
1516	}
1517    }
1518  return [NSData data];
1519}
1520
1521/**
1522 *  Returns whether have currently read through all of data buffer or file
1523 *  this unarchiver was initialized with.
1524 */
1525- (BOOL) isAtEnd
1526{
1527  return (cursor >= [data length]);
1528}
1529
1530/**
1531 *  Returns zone unarchived objects will be allocated from.
1532 */
1533- (NSZone*) objectZone
1534{
1535  return zone;
1536}
1537
1538/**
1539 *  Sets zone unarchived objects will be allocated from.
1540 */
1541- (void) setObjectZone: (NSZone*)aZone
1542{
1543  zone = aZone;
1544}
1545
1546/**
1547 *  Returns system version archive was encoded by.
1548 */
1549- (unsigned) systemVersion
1550{
1551  return version;
1552}
1553
1554/**
1555 *  Returns class name unarchivers will use to instantiate encoded objects
1556 *  when they report their class name as nameInArchive.
1557 */
1558+ (NSString*) classNameDecodedForArchiveClassName: (NSString*)nameInArchive
1559{
1560  NSUnarchiverClassInfo	*info = [clsDict objectForKey: nameInArchive];
1561  NSString		*alias;
1562
1563  if (info == nil)
1564    {
1565      return nil;
1566    }
1567  alias = info->name;
1568  if (alias)
1569    {
1570      return alias;
1571    }
1572  return nameInArchive;
1573}
1574
1575/**
1576 *  Sets class name unarchivers will use to instantiate encoded objects
1577 *  when they report their class name as nameInArchive.  This can be used
1578 *  to support backwards compatibility across class name changes.
1579 */
1580+ (void) decodeClassName: (NSString*)nameInArchive
1581	     asClassName: (NSString*)trueName
1582{
1583  Class	c;
1584
1585  c = objc_lookUpClass([trueName cString]);
1586  if (c == 0)
1587    {
1588      [NSException raise: NSInvalidArgumentException
1589		  format: @"can't find class %@", trueName];
1590    }
1591  else
1592    {
1593      NSUnarchiverClassInfo	*info = [clsDict objectForKey: nameInArchive];
1594
1595      if (info == nil)
1596	{
1597	  info = [NSUnarchiverClassInfo newWithName: nameInArchive];
1598	  [clsDict setObject: info forKey: nameInArchive];
1599	  RELEASE(info);
1600	}
1601      [info mapToClass: c withName: trueName];
1602    }
1603}
1604
1605/**
1606 *  Returns class name this unarchiver uses to instantiate encoded objects
1607 *  when they report their class name as nameInArchive.
1608 */
1609- (NSString*) classNameDecodedForArchiveClassName: (NSString*)nameInArchive
1610{
1611  NSUnarchiverObjectInfo	*info = [objDict objectForKey: nameInArchive];
1612  NSString			*alias;
1613
1614  if (info == nil)
1615    {
1616      return nil;
1617    }
1618  alias = mapClassName(info);
1619  if (alias)
1620    {
1621      return alias;
1622    }
1623  return nameInArchive;
1624}
1625
1626
1627/**
1628 *  Set class name this unarchiver uses to instantiate encoded objects
1629 *  when they report their class name as nameInArchive.  This can be used
1630 *  to provide backward compatibility across class name changes.
1631 */
1632- (void) decodeClassName: (NSString*)nameInArchive
1633	     asClassName: (NSString*)trueName
1634{
1635  Class	c;
1636
1637  c = objc_lookUpClass([trueName cString]);
1638  if (c == 0)
1639    {
1640      [NSException raise: NSInvalidArgumentException
1641		  format: @"can't find class %@", trueName];
1642    }
1643  else
1644    {
1645      NSUnarchiverObjectInfo	*info = [objDict objectForKey: nameInArchive];
1646
1647      if (info == nil)
1648	{
1649	  info = [NSUnarchiverObjectInfo newWithName: nameInArchive];
1650	  [objDict setObject: info forKey: nameInArchive];
1651	  RELEASE(info);
1652	}
1653      [info mapToClass: c withName: trueName];
1654    }
1655}
1656
1657/**
1658 *  Set unarchiver to replace anObject with replacement whenever it is
1659 *  found decoded from the archive.
1660 */
1661- (void) replaceObject: (id)anObject withObject: (id)replacement
1662{
1663  unsigned i;
1664
1665  if (replacement == anObject)
1666    return;
1667  for (i = GSIArrayCount(objMap) - 1; i > 0; i--)
1668    {
1669      if (GSIArrayItemAtIndex(objMap, i).obj == anObject)
1670	{
1671	  GSIArraySetItemAtIndex(objMap, (GSIArrayItem)replacement, i);
1672	  return;
1673	}
1674    }
1675  [NSException raise: NSInvalidArgumentException
1676	      format: @"object to be replaced does not exist"];
1677}
1678
1679- (NSInteger) versionForClassName: (NSString*)className
1680{
1681  NSUnarchiverObjectInfo	*info;
1682
1683  info = [objDict objectForKey: className];
1684  if (info == nil)
1685    {
1686      return (NSInteger)NSNotFound;
1687    }
1688  return info->version;
1689}
1690
1691@end
1692
1693
1694
1695
1696/**
1697 *  Category for compatibility with old GNUstep encoding.
1698 */
1699@implementation	NSUnarchiver (GNUstep)
1700
1701/**
1702 *  Return current position within archive byte array.
1703 */
1704- (unsigned) cursor
1705{
1706  return cursor;
1707}
1708
1709
1710/**
1711 *  Prepare for reuse of the unarchiver to unpack a new archive, specified in
1712 *  anObject, starting at pos.  Reads archive header.
1713 */
1714- (void) resetUnarchiverWithData: (NSData*)anObject
1715			 atIndex: (unsigned)pos
1716{
1717  unsigned	sizeC;
1718  unsigned	sizeO;
1719  unsigned	sizeP;
1720
1721  if (anObject == nil)
1722    {
1723      [NSException raise: NSInvalidArgumentException
1724		  format: @"nil passed to resetUnarchiverWithData:atIndex:"];
1725    }
1726  if (data != anObject)
1727    {
1728      Class	c;
1729
1730      TEST_RELEASE(data);
1731      data = RETAIN(anObject);
1732      c = object_getClass(data);
1733      if (src != self)
1734	{
1735	  src = data;
1736	  if (c != dataClass)
1737	    {
1738	      /*
1739	       *	Cache methods for deserialising from the data object.
1740	       */
1741	      desImp = [src methodForSelector: desSel];
1742	      tagImp = (void (*)(id, SEL, unsigned char*, unsigned*, unsigned*))
1743		  [src methodForSelector: tagSel];
1744	    }
1745	}
1746      dataClass = c;
1747    }
1748
1749  /*
1750   *	Read header including version and crossref table sizes.
1751   */
1752  cursor = pos;
1753  [self deserializeHeaderAt: &cursor
1754		    version: &version
1755		    classes: &sizeC
1756		    objects: &sizeO
1757		   pointers: &sizeP];
1758  if (version > encodingVersion)
1759    {
1760      [NSException raise: NSInvalidArgumentException
1761	format: @"Archive systemVersion (%u) not recognised", version];
1762    }
1763
1764  if (clsMap == 0)
1765    {
1766      /*
1767       *	Allocate and initialise arrays to build crossref maps in.
1768       */
1769      clsMap = NSZoneMalloc(zone, sizeof(GSIArray_t)*3);
1770      GSIArrayInitWithZoneAndCapacity(clsMap, zone, sizeC);
1771      GSIArrayAddItem(clsMap, (GSIArrayItem)(void*)0);
1772
1773      objMap = &clsMap[1];
1774      GSIArrayInitWithZoneAndCapacity(objMap, zone, sizeO);
1775      GSIArrayAddItem(objMap, (GSIArrayItem)(void*)0);
1776
1777      ptrMap = &clsMap[2];
1778      GSIArrayInitWithZoneAndCapacity(ptrMap, zone, sizeP);
1779      GSIArrayAddItem(ptrMap, (GSIArrayItem)(void*)0);
1780    }
1781  else
1782    {
1783      clsMap->count = 1;
1784      objMap->count = 1;
1785      ptrMap->count = 1;
1786    }
1787
1788  [objDict removeAllObjects];
1789  [objSave removeAllObjects];
1790}
1791
1792/**
1793 *  Reads in header for GNUstep archive format.
1794 */
1795- (void) deserializeHeaderAt: (unsigned*)pos
1796		     version: (unsigned*)v
1797		     classes: (unsigned*)c
1798		     objects: (unsigned*)o
1799		    pointers: (unsigned*)p
1800{
1801  unsigned	plen = strlen(PREFIX);
1802  unsigned	size = plen+36;
1803  char		header[size+1];
1804
1805  [data getBytes: header range: NSMakeRange(*pos, size)];
1806  *pos += size;
1807  header[size] = '\0';
1808  if (strncmp(header, PREFIX, plen) != 0)
1809    {
1810      [NSException raise: NSInternalInconsistencyException
1811		  format: @"Archive has wrong prefix"];
1812    }
1813  if (sscanf(&header[plen], "%x:%x:%x:%x:", v, c, o, p) != 4)
1814    {
1815      [NSException raise: NSInternalInconsistencyException
1816		  format: @"Archive has wrong prefix"];
1817    }
1818}
1819
1820/**
1821 *  Returns YES.
1822 */
1823- (BOOL) directDataAccess
1824{
1825  return YES;
1826}
1827
1828@end
1829
1830