1/** Implementation of NSArchiver for GNUstep
2   Copyright (C) 1998,1999 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>NSArchiver 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_NSArchiver_IVARS	1
35#define	EXPOSE_NSUnarchiver_IVARS	1
36/*
37 *	Setup for inline operation of pointer map tables.
38 */
39#define	GSI_MAP_KTYPES	GSUNION_NSINT | GSUNION_PTR | GSUNION_OBJ | GSUNION_CLS
40#define	GSI_MAP_VTYPES	GSUNION_NSINT | GSUNION_PTR | GSUNION_OBJ
41#define	GSI_MAP_RETAIN_KEY(M, X)
42#define	GSI_MAP_RELEASE_KEY(M, X)
43#define	GSI_MAP_RETAIN_VAL(M, X)
44#define	GSI_MAP_RELEASE_VAL(M, X)
45#define	GSI_MAP_HASH(M, X)	((X).nsu)
46#define	GSI_MAP_EQUAL(M, X,Y)	((X).ptr == (Y).ptr)
47#define	GSI_MAP_NOCLEAN	1
48
49
50#include "GNUstepBase/GSIMap.h"
51
52#define	_IN_NSARCHIVER_M
53#import "Foundation/NSArchiver.h"
54#undef	_IN_NSARCHIVER_M
55
56#import "Foundation/NSCoder.h"
57#import "Foundation/NSData.h"
58#import "Foundation/NSException.h"
59
60typedef	unsigned char	uchar;
61
62NSString * const NSInconsistentArchiveException =
63  @"NSInconsistentArchiveException";
64
65#define	PREFIX		"GNUstep archive"
66
67static SEL serSel;
68static SEL tagSel;
69static SEL xRefSel;
70static SEL eObjSel;
71static SEL eValSel;
72
73@class NSMutableDataMalloc;
74@interface NSMutableDataMalloc : NSObject	// Help the compiler
75@end
76static Class	NSMutableDataMallocClass;
77
78/**
79 *  <p>Implementation of [NSCoder] capable of creating sequential archives which
80 *  must be read in the same order they were written.  This class implements
81 *  methods for saving to and restoring from a serial archive (usually a file
82 *  on disk, but can be an [NSData] object) as well as methods that can be
83 *  used by objects that need to write/restore themselves.</p>
84 *
85 * <p>Note, the sibling class [NSKeyedArchiver] supports a form of archive
86 *  that is more robust to class changes, and is recommended over this one.</p>
87 */
88@implementation NSArchiver
89
90+ (void) initialize
91{
92  if (self == [NSArchiver class])
93    {
94      serSel = @selector(serializeDataAt:ofObjCType:context:);
95      tagSel = @selector(serializeTypeTag:);
96      xRefSel = @selector(serializeTypeTag:andCrossRef:);
97      eObjSel = @selector(encodeObject:);
98      eValSel = @selector(encodeValueOfObjCType:at:);
99      NSMutableDataMallocClass = [NSMutableDataMalloc class];
100    }
101}
102
103/**
104 *  Creates an NSMutableData instance and calls
105 *  [initForWritingWithMutableData:].
106 */
107- (id) init
108{
109  NSMutableData	*d;
110
111  d = [[NSMutableDataMallocClass allocWithZone: [self zone]] init];
112  self = [self initForWritingWithMutableData: d];
113  RELEASE(d);
114  return self;
115}
116
117/**
118 *  Init instance that will archive its data to mdata.  (Even if
119 *  [archiveRootObject:toFile:] is called, this still gets written to.)
120 */
121- (id) initForWritingWithMutableData: (NSMutableData*)mdata
122{
123  self = [super init];
124  if (self)
125    {
126      NSZone		*zone = [self zone];
127
128      _data = RETAIN(mdata);
129      if ([self directDataAccess] == YES)
130        {
131	  _dst = _data;
132	}
133      else
134	{
135	  _dst = self;
136	}
137      _serImp = [_dst methodForSelector: serSel];
138      _tagImp = [_dst methodForSelector: tagSel];
139      _xRefImp = [_dst methodForSelector: xRefSel];
140      _eObjImp = [self methodForSelector: eObjSel];
141      _eValImp = [self methodForSelector: eValSel];
142
143      [self resetArchiver];
144
145      /*
146       *	Set up map tables.
147       */
148      _clsMap = (GSIMapTable)NSZoneMalloc(zone, sizeof(GSIMapTable_t)*6);
149      _cIdMap = &_clsMap[1];
150      _uIdMap = &_clsMap[2];
151      _ptrMap = &_clsMap[3];
152      _namMap = &_clsMap[4];
153      _repMap = &_clsMap[5];
154      GSIMapInitWithZoneAndCapacity(_clsMap, zone, 100);
155      GSIMapInitWithZoneAndCapacity(_cIdMap, zone, 10);
156      GSIMapInitWithZoneAndCapacity(_uIdMap, zone, 200);
157      GSIMapInitWithZoneAndCapacity(_ptrMap, zone, 100);
158      GSIMapInitWithZoneAndCapacity(_namMap, zone, 1);
159      GSIMapInitWithZoneAndCapacity(_repMap, zone, 1);
160    }
161  return self;
162}
163
164- (void) dealloc
165{
166  RELEASE(_data);
167  if (_clsMap)
168    {
169      GSIMapEmptyMap(_clsMap);
170      if (_cIdMap)
171	{
172	  GSIMapEmptyMap(_cIdMap);
173	}
174      if (_uIdMap)
175	{
176	  GSIMapEmptyMap(_uIdMap);
177	}
178      if (_ptrMap)
179	{
180	  GSIMapEmptyMap(_ptrMap);
181	}
182      if (_namMap)
183	{
184	  GSIMapEmptyMap(_namMap);
185	}
186      if (_repMap)
187	{
188	  GSIMapEmptyMap(_repMap);
189	}
190      NSZoneFree(_clsMap->zone, (void*)_clsMap);
191    }
192  [super dealloc];
193}
194
195/**
196 *  Writes serialized representation of object and, recursively, any
197 *  other objects it holds references to, to byte array.
198 */
199+ (NSData*) archivedDataWithRootObject: (id)rootObject
200{
201  NSArchiver	*archiver;
202  id		d;
203  NSZone	*z = NSDefaultMallocZone();
204
205  d = [[NSMutableDataMallocClass allocWithZone: z] initWithCapacity: 0];
206  if (d == nil)
207    {
208      return nil;
209    }
210  archiver = [[self allocWithZone: z] initForWritingWithMutableData: d];
211  RELEASE(d);
212  d = nil;
213  if (archiver)
214    {
215      NS_DURING
216	{
217	  [archiver encodeRootObject: rootObject];
218	  d = AUTORELEASE([archiver->_data copy]);
219	}
220      NS_HANDLER
221	{
222	  RELEASE(archiver);
223	  [localException raise];
224	}
225      NS_ENDHANDLER
226      RELEASE(archiver);
227    }
228
229  return d;
230}
231
232/**
233 *  Writes out serialized representation of object and, recursively, any
234 *  other objects it holds references to.
235 */
236+ (BOOL) archiveRootObject: (id)rootObject
237		    toFile: (NSString*)path
238{
239  id	d = [self archivedDataWithRootObject: rootObject];
240
241  return [d writeToFile: path atomically: YES];
242}
243
244- (void) encodeArrayOfObjCType: (const char*)type
245			 count: (NSUInteger)count
246			    at: (const void*)buf
247{
248  uint32_t      c;
249  uint8_t	bytes[20];
250  uint8_t	*bytePtr = 0;
251  uint8_t	byteCount = 0;
252  NSUInteger	i;
253  NSUInteger	offset = 0;
254  uint32_t	size;
255  uint32_t	version;
256  uchar		info;
257
258  type = GSSkipTypeQualifierAndLayoutInfo(type);
259  size = objc_sizeof_type(type);
260  version = [self systemVersion];
261
262  if (12402 == version)
263    {
264      NSUInteger	tmp = count;
265
266      bytes[sizeof(bytes) - ++byteCount] = (uint8_t)(tmp % 128);
267      tmp /= 128;
268      while (tmp > 0)
269	{
270	  bytes[sizeof(bytes) - ++byteCount] = (uint8_t)(128 | (tmp % 128));
271	  tmp /= 128;
272	}
273      bytePtr = &bytes[sizeof(bytes) - byteCount];
274    }
275
276  /* We normally store the count as a 32bit integer ... but if it's
277   * very big, we store 0xffffffff and then an additional 64bit value
278   * containing the actual count.
279   */
280  if (count >= 0xffffffff)
281    {
282      c = 0xffffffff;
283    }
284  else
285    {
286      c = count;
287    }
288
289  switch (*type)
290    {
291      case _C_ID:	info = _GSC_NONE;		break;
292      case _C_CHR:	info = _GSC_CHR;		break;
293      case _C_UCHR:	info = _GSC_UCHR; 		break;
294      case _C_SHT:	info = _GSC_SHT | _GSC_S_SHT;	break;
295      case _C_USHT:	info = _GSC_USHT | _GSC_S_SHT;	break;
296      case _C_INT:	info = _GSC_INT | _GSC_S_INT;	break;
297      case _C_UINT:	info = _GSC_UINT | _GSC_S_INT;	break;
298      case _C_LNG:	info = _GSC_LNG | _GSC_S_LNG;	break;
299      case _C_ULNG:	info = _GSC_ULNG | _GSC_S_LNG; break;
300      case _C_LNG_LNG:	info = _GSC_LNG_LNG | _GSC_S_LNG_LNG;	break;
301      case _C_ULNG_LNG:	info = _GSC_ULNG_LNG | _GSC_S_LNG_LNG;	break;
302      case _C_FLT:	info = _GSC_FLT;	break;
303      case _C_DBL:	info = _GSC_DBL;	break;
304#if __GNUC__ > 2 && defined(_C_BOOL)
305      case _C_BOOL:	info = _GSC_BOOL;	break;
306#endif
307      default:		info = _GSC_NONE;	break;
308    }
309
310  /*
311   *	Simple types can be serialized immediately, more complex ones
312   *	are dealt with by our [encodeValueOfObjCType:at:] method.
313   */
314  if (info == _GSC_NONE)
315    {
316      if (_initialPass == NO)
317	{
318	  (*_tagImp)(_dst, tagSel, _GSC_ARY_B);
319	  if (12402 == version)
320	    {
321	      for (i = 0; i < byteCount; i++)
322		{
323		  (*_serImp)(_dst, serSel, bytePtr + i, @encode(uint8_t), nil);
324		}
325	    }
326	  else
327	    {
328	      (*_serImp)(_dst, serSel, &c, @encode(uint32_t), nil);
329	      if (0xffffffff == c)
330		{
331		  (*_serImp)(_dst, serSel, &count, @encode(NSUInteger), nil);
332		}
333	    }
334	}
335
336      for (i = 0; i < c; i++)
337	{
338	  (*_eValImp)(self, eValSel, type, (char*)buf + offset);
339	  offset += size;
340	}
341    }
342  else if (_initialPass == NO)
343    {
344      (*_tagImp)(_dst, tagSel, _GSC_ARY_B);
345      if (12402 == version)
346	{
347	  for (i = 0; i < byteCount; i++)
348	    {
349	      (*_serImp)(_dst, serSel, bytePtr + i, @encode(uint8_t), nil);
350	    }
351	}
352      else
353	{
354	  (*_serImp)(_dst, serSel, &c, @encode(uint32_t), nil);
355	  if (0xffffffff == c)
356	    {
357	      (*_serImp)(_dst, serSel, &count, @encode(NSUInteger), nil);
358	    }
359	}
360
361      (*_tagImp)(_dst, tagSel, info);
362      for (i = 0; i < count; i++)
363	{
364	  (*_serImp)(_dst, serSel, (char*)buf + offset, type, nil);
365	  offset += size;
366	}
367    }
368}
369
370- (void) encodeValueOfObjCType: (const char*)type
371			    at: (const void*)buf
372{
373  type = GSSkipTypeQualifierAndLayoutInfo(type);
374  switch (*type)
375    {
376      case _C_ID:
377	(*_eObjImp)(self, eObjSel, *(void**)buf);
378	return;
379
380      case _C_ARY_B:
381	{
382	  unsigned	count = atoi(++type);
383
384	  while (isdigit(*type))
385	    {
386	      type++;
387	    }
388
389	  if (_initialPass == NO)
390	    {
391	      (*_tagImp)(_dst, tagSel, _GSC_ARY_B);
392	    }
393
394	  [self encodeArrayOfObjCType: type count: count at: buf];
395	}
396	return;
397
398      case _C_STRUCT_B:
399	{
400	  struct objc_struct_layout layout;
401
402	  if (_initialPass == NO)
403	    {
404	      (*_tagImp)(_dst, tagSel, _GSC_STRUCT_B);
405	    }
406	  objc_layout_structure (type, &layout);
407	  while (objc_layout_structure_next_member (&layout))
408	    {
409	      unsigned		offset;
410	      unsigned		align;
411	      const char	*ftype;
412
413	      objc_layout_structure_get_info (&layout, &offset, &align, &ftype);
414
415	      (*_eValImp)(self, eValSel, ftype, (char*)buf + offset);
416	    }
417	}
418	return;
419
420      case _C_PTR:
421	if (*(void**)buf == 0)
422	  {
423	    if (_initialPass == NO)
424	      {
425		/*
426		 *	Special case - a null pointer gets an xref of zero
427		 */
428		(*_tagImp)(_dst, tagSel, _GSC_PTR | _GSC_XREF | _GSC_X_0);
429	      }
430	  }
431	else
432	  {
433	    GSIMapNode	node;
434
435	    node = GSIMapNodeForKey(_ptrMap, (GSIMapKey)*(void**)buf);
436	    if (_initialPass == YES)
437	      {
438		/*
439		 *	First pass - add pointer to map and encode item pointed
440		 *	to in case it is a conditionally encoded object.
441		 */
442		if (node == 0)
443		  {
444		    GSIMapAddPair(_ptrMap,
445		      (GSIMapKey)*(void**)buf, (GSIMapVal)(NSUInteger)0);
446		    type++;
447		    buf = *(char**)buf;
448		    (*_eValImp)(self, eValSel, type, buf);
449		  }
450	      }
451	    else if (node == 0 || node->value.nsu == 0)
452	      {
453		/*
454		 *	Second pass, unwritten pointer - write it.
455		 */
456		if (node == 0)
457		  {
458		    node = GSIMapAddPair(_ptrMap,
459		      (GSIMapKey)*(void**)buf, (GSIMapVal)(NSUInteger)++_xRefP);
460		  }
461		else
462		  {
463		    node->value.nsu = ++_xRefP;
464		  }
465		(*_xRefImp)(_dst, xRefSel, _GSC_PTR, node->value.nsu);
466		type++;
467		buf = *(char**)buf;
468		(*_eValImp)(self, eValSel, type, buf);
469	      }
470	    else
471	      {
472		/*
473		 *	Second pass, write a cross-reference number.
474		 */
475		(*_xRefImp)(_dst, xRefSel, _GSC_PTR|_GSC_XREF,
476		  node->value.nsu);
477	      }
478	  }
479	return;
480
481      default:	/* Types that can be ignored in first pass.	*/
482	if (_initialPass)
483	  {
484	    return;
485	  }
486	break;
487    }
488
489  switch (*type)
490    {
491      case _C_CLASS:
492	if (*(Class*)buf == 0)
493	  {
494	    /*
495	     *	Special case - a null pointer gets an xref of zero
496	     */
497	    (*_tagImp)(_dst, tagSel, _GSC_CLASS | _GSC_XREF | _GSC_X_0);
498	  }
499	else
500	  {
501	    Class	c = *(Class*)buf;
502	    GSIMapNode	node;
503	    BOOL	done = NO;
504
505	    node = GSIMapNodeForKey(_clsMap, (GSIMapKey)(void*)c);
506
507	    if (node != 0)
508	      {
509		(*_xRefImp)(_dst, xRefSel, _GSC_CLASS | _GSC_XREF,
510		  node->value.nsu);
511		return;
512	      }
513	    while (done == NO)
514	      {
515		int		tmp = class_getVersion(c);
516		unsigned	version = tmp;
517		Class		s = class_getSuperclass(c);
518
519		if (tmp < 0)
520		  {
521		    [NSException raise: NSInternalInconsistencyException
522				format: @"negative class version"];
523		  }
524		node = GSIMapAddPair(_clsMap,
525		  (GSIMapKey)(void*)c, (GSIMapVal)(NSUInteger)++_xRefC);
526		/*
527		 *	Encode tag and crossref number.
528		 */
529		(*_xRefImp)(_dst, xRefSel, _GSC_CLASS, node->value.nsu);
530		/*
531		 *	Encode class, and version.
532		 */
533		(*_serImp)(_dst, serSel, &c, @encode(Class), nil);
534		(*_serImp)(_dst, serSel, &version, @encode(unsigned), nil);
535		/*
536		 *	If we have a super class that has not been encoded,
537		 *	we must loop round to encode it here so that its
538		 *	version information will be available when objects
539		 *	of its subclasses are decoded and call
540		 *	[super initWithCoder:ccc]
541		 */
542		if (s == c || s == 0
543		  || GSIMapNodeForKey(_clsMap, (GSIMapKey)(void*)s) != 0)
544		  {
545		    done = YES;
546		  }
547		else
548		  {
549		    c = s;
550		  }
551	      }
552	    /*
553	     *	Encode an empty tag to terminate the list of classes.
554	     */
555	    (*_tagImp)(_dst, tagSel, _GSC_NONE);
556	  }
557	return;
558
559      case _C_SEL:
560	if (*(SEL*)buf == 0)
561	  {
562	    /*
563	     *	Special case - a null pointer gets an xref of zero
564	     */
565	    (*_tagImp)(_dst, tagSel, _GSC_SEL | _GSC_XREF | _GSC_X_0);
566	  }
567	else
568	  {
569	    SEL		s = *(SEL*)buf;
570	    GSIMapNode	node = GSIMapNodeForKey(_ptrMap, (GSIMapKey)(void*)s);
571
572	    if (node == 0)
573	      {
574		node = GSIMapAddPair(_ptrMap,
575		  (GSIMapKey)(void*)s, (GSIMapVal)(NSUInteger)++_xRefP);
576		(*_xRefImp)(_dst, xRefSel, _GSC_SEL, node->value.nsu);
577		/*
578		 *	Encode selector.
579		 */
580		(*_serImp)(_dst, serSel, buf, @encode(SEL), nil);
581	      }
582	    else
583	      {
584		(*_xRefImp)(_dst, xRefSel, _GSC_SEL|_GSC_XREF,
585		  node->value.nsu);
586	      }
587	  }
588	return;
589
590      case _C_CHARPTR:
591	if (*(char**)buf == 0)
592	  {
593	    /*
594	     *	Special case - a null pointer gets an xref of zero
595	     */
596	    (*_tagImp)(_dst, tagSel, _GSC_CHARPTR | _GSC_XREF | _GSC_X_0);
597	  }
598	else
599	  {
600	    GSIMapNode	node;
601
602	    node = GSIMapNodeForKey(_ptrMap, (GSIMapKey)*(char**)buf);
603	    if (node == 0)
604	      {
605		node = GSIMapAddPair(_ptrMap,
606		  (GSIMapKey)*(char**)buf, (GSIMapVal)(NSUInteger)++_xRefP);
607		(*_xRefImp)(_dst, xRefSel, _GSC_CHARPTR, node->value.nsu);
608		(*_serImp)(_dst, serSel, buf, type, nil);
609	      }
610	    else
611	      {
612		(*_xRefImp)(_dst, xRefSel, _GSC_CHARPTR|_GSC_XREF,
613		  node->value.nsu);
614	      }
615	  }
616	return;
617
618      case _C_CHR:
619	(*_tagImp)(_dst, tagSel, _GSC_CHR);
620	(*_serImp)(_dst, serSel, (void*)buf, @encode(signed char), nil);
621	return;
622
623      case _C_UCHR:
624	(*_tagImp)(_dst, tagSel, _GSC_UCHR);
625	(*_serImp)(_dst, serSel, (void*)buf, @encode(unsigned char), nil);
626	return;
627
628      case _C_SHT:
629	(*_tagImp)(_dst, tagSel, _GSC_SHT | _GSC_S_SHT);
630	(*_serImp)(_dst, serSel, (void*)buf, @encode(short), nil);
631	return;
632
633      case _C_USHT:
634	(*_tagImp)(_dst, tagSel, _GSC_USHT | _GSC_S_SHT);
635	(*_serImp)(_dst, serSel, (void*)buf, @encode(unsigned short), nil);
636	return;
637
638      case _C_INT:
639	(*_tagImp)(_dst, tagSel, _GSC_INT | _GSC_S_INT);
640	(*_serImp)(_dst, serSel, (void*)buf, @encode(int), nil);
641	return;
642
643      case _C_UINT:
644	(*_tagImp)(_dst, tagSel, _GSC_UINT | _GSC_S_INT);
645	(*_serImp)(_dst, serSel, (void*)buf, @encode(unsigned int), nil);
646	return;
647
648      case _C_LNG:
649	(*_tagImp)(_dst, tagSel, _GSC_LNG | _GSC_S_LNG);
650	(*_serImp)(_dst, serSel, (void*)buf, @encode(long), nil);
651	return;
652
653      case _C_ULNG:
654	(*_tagImp)(_dst, tagSel, _GSC_ULNG | _GSC_S_LNG);
655	(*_serImp)(_dst, serSel, (void*)buf, @encode(unsigned long), nil);
656	return;
657
658      case _C_LNG_LNG:
659	(*_tagImp)(_dst, tagSel, _GSC_LNG_LNG | _GSC_S_LNG_LNG);
660	(*_serImp)(_dst, serSel, (void*)buf, @encode(long long), nil);
661	return;
662
663      case _C_ULNG_LNG:
664	(*_tagImp)(_dst, tagSel, _GSC_ULNG_LNG | _GSC_S_LNG_LNG);
665	(*_serImp)(_dst, serSel, (void*)buf, @encode(unsigned long long), nil);
666	return;
667
668      case _C_FLT:
669	(*_tagImp)(_dst, tagSel, _GSC_FLT);
670	(*_serImp)(_dst, serSel, (void*)buf, @encode(float), nil);
671	return;
672
673      case _C_DBL:
674	(*_tagImp)(_dst, tagSel, _GSC_DBL);
675	(*_serImp)(_dst, serSel, (void*)buf, @encode(double), nil);
676	return;
677
678#if __GNUC__ > 2 && defined(_C_BOOL)
679      case _C_BOOL:
680	(*_tagImp)(_dst, tagSel, _GSC_BOOL);
681	(*_serImp)(_dst, serSel, (void*)buf, @encode(_Bool), nil);
682	return;
683#endif
684
685      case _C_VOID:
686	[NSException raise: NSInvalidArgumentException
687		    format: @"can't encode void item"];
688
689      default:
690	[NSException raise: NSInvalidArgumentException
691		    format: @"item with unknown type - %s", type];
692    }
693}
694
695- (void) encodeRootObject: (id)rootObject
696{
697  if (_encodingRoot)
698    {
699      [NSException raise: NSInvalidArgumentException
700		  format: @"encoding root object more than once"];
701    }
702
703  _encodingRoot = YES;
704
705  /*
706   *	First pass - find conditional objects.
707   */
708  _initialPass = YES;
709  (*_eObjImp)(self, eObjSel, rootObject);
710
711  /*
712   *	Second pass - write archive.
713   */
714  _initialPass = NO;
715  (*_eObjImp)(self, eObjSel, rootObject);
716
717  /*
718   *	Write sizes of crossref arrays to head of archive.
719   */
720  [self serializeHeaderAt: _startPos
721		  version: [self systemVersion]
722		  classes: _clsMap->nodeCount
723		  objects: _uIdMap->nodeCount
724		 pointers: _ptrMap->nodeCount];
725
726  _encodingRoot = NO;
727}
728
729- (void) encodeConditionalObject: (id)anObject
730{
731  if (_encodingRoot == NO)
732    {
733      [NSException raise: NSInvalidArgumentException
734		  format: @"conditionally encoding without root object"];
735      return;
736    }
737
738  if (_initialPass)
739    {
740      GSIMapNode	node;
741
742      /*
743       *	Conditionally encoding 'nil' is a no-op.
744       */
745      if (anObject == nil)
746	{
747	  return;
748	}
749
750      /*
751       *	If we have already conditionally encoded this object, we can
752       *	ignore it this time.
753       */
754      node = GSIMapNodeForKey(_cIdMap, (GSIMapKey)anObject);
755      if (node != 0)
756	{
757	  return;
758	}
759
760      /*
761       *	If we have unconditionally encoded this object, we can ignore
762       *	it now.
763       */
764      node = GSIMapNodeForKey(_uIdMap, (GSIMapKey)anObject);
765      if (node != 0)
766	{
767	  return;
768	}
769
770      GSIMapAddPair(_cIdMap, (GSIMapKey)anObject, (GSIMapVal)(NSUInteger)0);
771    }
772  else if (anObject == nil)
773    {
774      (*_eObjImp)(self, eObjSel, nil);
775    }
776  else
777    {
778      GSIMapNode	node;
779
780      if (_repMap->nodeCount)
781	{
782	  node = GSIMapNodeForKey(_repMap, (GSIMapKey)anObject);
783	  if (node)
784	    {
785	      anObject = (id)node->value.ptr;
786	    }
787	}
788
789      node = GSIMapNodeForKey(_cIdMap, (GSIMapKey)anObject);
790      if (node != 0)
791	{
792	  (*_eObjImp)(self, eObjSel, nil);
793	}
794      else
795	{
796	  (*_eObjImp)(self, eObjSel, anObject);
797	}
798    }
799}
800
801- (void) encodeDataObject: (NSData*)anObject
802{
803  unsigned	l = [anObject length];
804
805  (*_eValImp)(self, eValSel, @encode(unsigned int), &l);
806  if (l)
807    {
808      const void	*b = [anObject bytes];
809      unsigned char	c = 0;			/* Type tag	*/
810
811      /*
812       * The type tag 'c' is used to specify an encoding scheme for the
813       * actual data - at present we have '0' meaning raw data.  In the
814       * future we might want zipped data for instance.
815       */
816      (*_eValImp)(self, eValSel, @encode(unsigned char), &c);
817      [self encodeArrayOfObjCType: @encode(unsigned char)
818			    count: l
819			       at: b];
820    }
821}
822
823- (void) encodeObject: (id)anObject
824{
825  if (anObject == nil)
826    {
827      if (_initialPass == NO)
828	{
829	  /*
830	   *	Special case - encode a nil pointer as a crossref of zero.
831	   */
832	  (*_tagImp)(_dst, tagSel, _GSC_ID | _GSC_XREF, _GSC_X_0);
833	}
834    }
835  else
836    {
837      GSIMapNode	node;
838
839      /*
840       *	Substitute replacement object if required.
841       */
842      node = GSIMapNodeForKey(_repMap, (GSIMapKey)anObject);
843      if (node)
844	{
845	  anObject = (id)node->value.ptr;
846	}
847
848      /*
849       *	See if the object has already been encoded.
850       */
851      node = GSIMapNodeForKey(_uIdMap, (GSIMapKey)anObject);
852
853      if (_initialPass)
854	{
855	  if (node == 0)
856	    {
857	      /*
858	       *	Remove object from map of conditionally encoded objects
859	       *	and add it to the map of unconditionay encoded ones.
860	       */
861	      GSIMapRemoveKey(_cIdMap, (GSIMapKey)anObject);
862	      GSIMapAddPair(_uIdMap,
863		(GSIMapKey)anObject, (GSIMapVal)(NSUInteger)0);
864	      [anObject encodeWithCoder: self];
865	    }
866	  return;
867	}
868
869      if (node == 0 || node->value.nsu == 0)
870	{
871	  Class	cls;
872	  id	obj;
873
874	  if (node == 0)
875	    {
876	      node = GSIMapAddPair(_uIdMap,
877		(GSIMapKey)anObject, (GSIMapVal)(NSUInteger)++_xRefO);
878	    }
879	  else
880	    {
881	      node->value.nsu = ++_xRefO;
882	    }
883
884	  obj = [anObject replacementObjectForArchiver: self];
885	  if (GSObjCIsInstance(obj) == NO)
886	    {
887	      /*
888	       * If the object we have been given is actually a class,
889	       * we encode it as a special case.
890	       */
891	      (*_xRefImp)(_dst, xRefSel, _GSC_CID, node->value.nsu);
892	      (*_eValImp)(self, eValSel, @encode(Class), &obj);
893	    }
894	  else
895	    {
896	      cls = [obj classForArchiver];
897	      if (_namMap->nodeCount)
898		{
899		  GSIMapNode	n;
900
901		  n = GSIMapNodeForKey(_namMap, (GSIMapKey)cls);
902
903		  if (n)
904		    {
905		      cls = (Class)n->value.ptr;
906		    }
907		}
908	      (*_xRefImp)(_dst, xRefSel, _GSC_ID, node->value.nsu);
909	      (*_eValImp)(self, eValSel, @encode(Class), &cls);
910	      [obj encodeWithCoder: self];
911	    }
912	}
913      else
914	{
915	  (*_xRefImp)(_dst, xRefSel, _GSC_ID | _GSC_XREF, node->value.nsu);
916	}
917    }
918}
919
920/**
921 *  Returns whatever data has been encoded thus far.
922 */
923- (NSMutableData*) archiverData
924{
925  return _data;
926}
927
928/**
929 *  Returns substitute class used to encode objects of given class.  This
930 *  would have been set through an earlier call to
931 *  [NSArchiver -encodeClassName:intoClassName:].
932 */
933- (NSString*) classNameEncodedForTrueClassName: (NSString*)trueName
934{
935  if (_namMap->nodeCount)
936    {
937      GSIMapNode	node;
938      Class		c;
939
940      c = objc_lookUpClass([trueName cString]);
941      node = GSIMapNodeForKey(_namMap, (GSIMapKey)c);
942      if (node)
943	{
944	  c = (Class)node->value.ptr;
945	  return [NSString stringWithUTF8String: class_getName(c)];
946	}
947    }
948  return trueName;
949}
950
951/**
952 *  Specify substitute class used in archiving objects of given class.  This
953 *  class is written to the archive as the class to use for restoring the
954 *  object, instead of what is returned from [NSObject -classForArchiver].
955 *  This can be used to provide backward compatibility across class name
956 *  changes.  The object is still encoded by calling
957 *  <code>encodeWithCoder:</code> as normal.
958 */
959- (void) encodeClassName: (NSString*)trueName
960	   intoClassName: (NSString*)inArchiveName
961{
962  GSIMapNode	node;
963  Class		tc;
964  Class		ic;
965
966  tc = objc_lookUpClass([trueName cString]);
967  if (tc == 0)
968    {
969      [NSException raise: NSInternalInconsistencyException
970		  format: @"Can't find class '%@'.", trueName];
971    }
972  ic = objc_lookUpClass([inArchiveName cString]);
973  if (ic == 0)
974    {
975      [NSException raise: NSInternalInconsistencyException
976		  format: @"Can't find class '%@'.", inArchiveName];
977    }
978  node = GSIMapNodeForKey(_namMap, (GSIMapKey)tc);
979  if (node == 0)
980    {
981      GSIMapAddPair(_namMap, (GSIMapKey)(void*)tc, (GSIMapVal)(void*)ic);
982    }
983  else
984    {
985      node->value.ptr = (void*)ic;
986    }
987}
988
989/**
990 *  Set encoder to write out newObject in place of object.
991 */
992- (void) replaceObject: (id)object
993	    withObject: (id)newObject
994{
995  GSIMapNode	node;
996
997  if (object == 0)
998    {
999      [NSException raise: NSInternalInconsistencyException
1000		  format: @"attempt to remap nil"];
1001    }
1002  if (newObject == 0)
1003    {
1004      [NSException raise: NSInternalInconsistencyException
1005		  format: @"attempt to remap object to nil"];
1006    }
1007  node = GSIMapNodeForKey(_repMap, (GSIMapKey)object);
1008  if (node == 0)
1009    {
1010      GSIMapAddPair(_repMap, (GSIMapKey)object, (GSIMapVal)newObject);
1011    }
1012  else
1013    {
1014      node->value.ptr = (void*)newObject;
1015    }
1016}
1017@end
1018
1019
1020
1021/**
1022 *  Category for compatibility with old GNUstep encoding.
1023 */
1024@implementation	NSArchiver (GNUstep)
1025
1026/**
1027 *  Allow reuse of archiver (clears class substitution maps, etc.) but
1028 *  do not clear out current serialized data.
1029 */
1030- (void) resetArchiver
1031{
1032  if (_clsMap)
1033    {
1034      GSIMapCleanMap(_clsMap);
1035      if (_cIdMap)
1036	{
1037	  GSIMapCleanMap(_cIdMap);
1038	}
1039      if (_uIdMap)
1040	{
1041	  GSIMapCleanMap(_uIdMap);
1042	}
1043      if (_ptrMap)
1044	{
1045	  GSIMapCleanMap(_ptrMap);
1046	}
1047      if (_namMap)
1048	{
1049	  GSIMapCleanMap(_namMap);
1050	}
1051      if (_repMap)
1052	{
1053	  GSIMapCleanMap(_repMap);
1054	}
1055    }
1056  _encodingRoot = NO;
1057  _initialPass = NO;
1058  _xRefC = 0;
1059  _xRefO = 0;
1060  _xRefP = 0;
1061
1062  /*
1063   *	Write dummy header
1064   */
1065  _startPos = [_data length];
1066  [self serializeHeaderAt: _startPos
1067		  version: [self systemVersion]
1068		  classes: 0
1069		  objects: 0
1070		 pointers: 0];
1071}
1072
1073/**
1074 *  Returns YES.
1075 */
1076- (BOOL) directDataAccess
1077{
1078  return YES;
1079}
1080
1081/**
1082 *  Writes out header for GNUstep archive format.
1083 */
1084- (void) serializeHeaderAt: (unsigned)positionInData
1085		   version: (unsigned)systemVersion
1086		   classes: (unsigned)classCount
1087		   objects: (unsigned)objectCount
1088		  pointers: (unsigned)pointerCount
1089{
1090  unsigned	headerLength = strlen(PREFIX)+36;
1091  char		header[headerLength+1];
1092  unsigned	dataLength = [_data length];
1093
1094  snprintf(header, sizeof(header), "%s%08x:%08x:%08x:%08x:",
1095    PREFIX, systemVersion, classCount, objectCount, pointerCount);
1096
1097  if (positionInData + headerLength <= dataLength)
1098    {
1099      [_data replaceBytesInRange: NSMakeRange(positionInData, headerLength)
1100		      withBytes: header];
1101    }
1102  else if (positionInData == dataLength)
1103    {
1104      [_data appendBytes: header length: headerLength];
1105    }
1106  else
1107    {
1108      [NSException raise: NSInternalInconsistencyException
1109		  format: @"serializeHeader:at: bad location"];
1110    }
1111}
1112
1113@end
1114
1115