mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-26 18:21:04 +00:00
if object satisfies a forward reference *after* encoding it. This is necessary because the encoding of subobjects may generate forward references. (In a sense, this undoes the last change, but it is significantly different in that we test whether the object is in the object_2_fref table *after* encoding the object; before we looked in the table before encoding the object.) As a consequence, the CODER_OBJECT_FORWARD_SATISFIER tag is no longer necessary; this was used to tag an object before it was encoded, but now we understand that we cannot know whether this object satisfies a forward reference until after it has been encoded. Thus, for all objects we encode an unsigned int after encoding the object itself. This unsigned int is either the forward reference number of zero, depending on whether this object satisfies any forward references. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1358 72102866-910b-0410-8b05-ffd578937521
827 lines
22 KiB
Objective-C
827 lines
22 KiB
Objective-C
/* Abstract class for writing objects to a stream
|
||
Copyright (C) 1996 Free Software Foundation, Inc.
|
||
|
||
Written by: R. Andrew McCallum <mccallum@gnu.ai.mit.edu>
|
||
Created: February 1996, with core from Coder, created 1994.
|
||
|
||
This file is part of the GNU Objective C Class Library.
|
||
|
||
This library is free software; you can redistribute it and/or
|
||
modify it under the terms of the GNU Library General Public
|
||
License as published by the Free Software Foundation; either
|
||
version 2 of the License, or (at your option) any later version.
|
||
|
||
This library is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
Library General Public License for more details.
|
||
|
||
You should have received a copy of the GNU Library General Public
|
||
License along with this library; if not, write to the Free
|
||
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
*/
|
||
|
||
#include <objects/stdobjects.h>
|
||
#include <objects/Coder.h>
|
||
#include <objects/CoderPrivate.h>
|
||
#include <objects/MemoryStream.h>
|
||
#include <objects/StdioStream.h>
|
||
#include <objects/BinaryCStream.h>
|
||
#include <Foundation/NSArchiver.h>
|
||
#include <Foundation/NSException.h>
|
||
|
||
static int default_format_version;
|
||
static id default_stream_class;
|
||
static id default_cstream_class;
|
||
|
||
#define debug_coder 0
|
||
#define DEFAULT_FORMAT_VERSION 0
|
||
|
||
|
||
/* xxx For experimentation. The function in objc-api.h doesn't always
|
||
work for objects; it sometimes returns YES for an instance. */
|
||
/* But, metaclasses return YES too? */
|
||
static BOOL
|
||
my_object_is_class(id object)
|
||
{
|
||
if (object != nil
|
||
#if NeXT_runtime
|
||
&& CLS_ISMETA(((Class)object)->isa)
|
||
&& ((Class)object)->isa != ((Class)object)->isa)
|
||
#else
|
||
&& CLS_ISMETA(((Class)object)->class_pointer)
|
||
&& ((Class)object)->class_pointer != ((Class)object)->class_pointer)
|
||
#endif
|
||
return YES;
|
||
else
|
||
return NO;
|
||
}
|
||
|
||
|
||
@implementation Encoder
|
||
|
||
+ (void) initialize
|
||
{
|
||
if (self == [Encoder class])
|
||
{
|
||
/* This code has not yet been ported to machines for which
|
||
a pointer is not the same size as an int. */
|
||
assert(sizeof(void*) == sizeof(unsigned));
|
||
|
||
/* Initialize some defaults. */
|
||
default_stream_class = [MemoryStream class];
|
||
default_cstream_class = [BinaryCStream class];
|
||
default_format_version = DEFAULT_FORMAT_VERSION;
|
||
}
|
||
}
|
||
|
||
|
||
/* Default format version, Stream and CStream class handling. */
|
||
|
||
+ (int) defaultFormatVersion
|
||
{
|
||
return default_format_version;
|
||
}
|
||
|
||
+ (void) setDefaultFormatVersion: (int)f
|
||
{
|
||
default_format_version = f;
|
||
}
|
||
|
||
+ (void) setDefaultCStreamClass: sc
|
||
{
|
||
default_cstream_class = sc;
|
||
}
|
||
|
||
+ defaultCStreamClass
|
||
{
|
||
return default_cstream_class;
|
||
}
|
||
|
||
+ (void) setDefaultStreamClass: sc
|
||
{
|
||
default_stream_class = sc;
|
||
}
|
||
|
||
+ defaultStreamClass
|
||
{
|
||
return default_stream_class;
|
||
}
|
||
|
||
/* xxx This method interface may change in the future. */
|
||
- (const char *) defaultDecoderClassname
|
||
{
|
||
return "Unarchiver";
|
||
}
|
||
|
||
|
||
/* Signature Handling. */
|
||
|
||
- (void) writeSignature
|
||
{
|
||
/* Careful: the string should not contain newlines. */
|
||
[[cstream stream] writeFormat: SIGNATURE_FORMAT_STRING,
|
||
WRITE_SIGNATURE_FORMAT_ARGS];
|
||
}
|
||
|
||
|
||
|
||
/* This is the designated initializer for this class. */
|
||
- initForWritingToStream: (id <Streaming>) s
|
||
withFormatVersion: (int) version
|
||
cStreamClass: (Class) cStreamClass
|
||
cStreamFormatVersion: (int) cStreamFormatVersion
|
||
{
|
||
[super _initWithCStream: [[cStreamClass alloc]
|
||
initForWritingToStream: s
|
||
withFormatVersion: cStreamFormatVersion]
|
||
formatVersion: version];
|
||
in_progress_table = NULL;
|
||
object_2_xref = NULL;
|
||
object_2_fref = NULL;
|
||
const_ptr_2_xref = NULL;
|
||
fref_counter = 0;
|
||
[self writeSignature];
|
||
return self;
|
||
}
|
||
|
||
/* ..Writing... methods */
|
||
|
||
- initForWritingToStream: (id <Streaming>) s
|
||
withCStreamClass: (Class) cStreamClass
|
||
{
|
||
return [self initForWritingToStream: s
|
||
withFormatVersion: DEFAULT_FORMAT_VERSION
|
||
cStreamClass: cStreamClass
|
||
cStreamFormatVersion: [cStreamClass defaultFormatVersion]];
|
||
}
|
||
|
||
- initForWritingToStream: (id <Streaming>) s
|
||
{
|
||
return [self initForWritingToStream: s
|
||
withCStreamClass: [[self class] defaultCStreamClass]];
|
||
}
|
||
|
||
- initForWritingToFile: (id <String>) filename
|
||
withFormatVersion: (int) version
|
||
cStreamClass: (Class) cStreamClass
|
||
cStreamFormatVersion: (int) cStreamFormatVersion
|
||
{
|
||
return [self initForWritingToStream: [StdioStream
|
||
streamWithFilename: filename
|
||
fmode: "w"]
|
||
withFormatVersion: version
|
||
cStreamClass: cStreamClass
|
||
cStreamFormatVersion: cStreamFormatVersion];
|
||
}
|
||
|
||
- initForWritingToFile: (id <String>) filename
|
||
withCStreamClass: (Class) cStreamClass
|
||
{
|
||
return [self initForWritingToStream: [StdioStream
|
||
streamWithFilename: filename
|
||
fmode: "w"]
|
||
withCStreamClass: cStreamClass];
|
||
}
|
||
|
||
- initForWritingToFile: (id <String>) filename
|
||
{
|
||
return [self initForWritingToStream:
|
||
[StdioStream streamWithFilename: filename
|
||
fmode: "w"]];
|
||
}
|
||
|
||
+ newWritingToStream: (id <Streaming>)s
|
||
{
|
||
return [[self alloc] initForWritingToStream: s];
|
||
}
|
||
|
||
+ newWritingToFile: (id <String>)filename
|
||
{
|
||
return [self newWritingToStream:
|
||
[StdioStream streamWithFilename: filename
|
||
fmode: "w"]];
|
||
}
|
||
|
||
+ (BOOL) encodeRootObject: anObject
|
||
withName: (id <String>) name
|
||
toStream: (id <Streaming>)stream
|
||
{
|
||
id c = [[self alloc] initForWritingToStream: stream];
|
||
[c encodeRootObject: anObject withName: name];
|
||
[c close];
|
||
[c release];
|
||
return YES;
|
||
}
|
||
|
||
+ (BOOL) encodeRootObject: anObject
|
||
withName: (id <String>) name
|
||
toFile: (id <String>) filename
|
||
{
|
||
return [self encodeRootObject: anObject
|
||
withName: name
|
||
toStream: [StdioStream streamWithFilename: filename
|
||
fmode: "w"]];
|
||
}
|
||
|
||
|
||
/* Functions and methods for keeping cross-references
|
||
so objects aren't written/read twice. */
|
||
|
||
/* These _coder... methods may be overriden by subclasses so that
|
||
cross-references can be kept differently.
|
||
|
||
For instance, ConnectedCoder keeps cross-references to const
|
||
pointers on a per-Connection basis instead of a per-Coder basis.
|
||
We avoid encoding/decoding the same classes and selectors over and
|
||
over again.
|
||
*/
|
||
- (unsigned) _coderCreateReferenceForObject: anObj
|
||
{
|
||
unsigned xref;
|
||
if (!object_2_xref)
|
||
{
|
||
object_2_xref =
|
||
NSCreateMapTable (NSNonOwnedPointerOrNullMapKeyCallBacks,
|
||
NSIntMapValueCallBacks, 0);
|
||
}
|
||
xref = NSCountMapTable (object_2_xref) + 1;
|
||
NSMapInsert (object_2_xref, anObj, (void*)xref);
|
||
return xref;
|
||
}
|
||
|
||
- (unsigned) _coderReferenceForObject: anObject
|
||
{
|
||
if (object_2_xref)
|
||
return (unsigned) NSMapGet (object_2_xref, anObject);
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Methods for handling constant pointers */
|
||
/* By overriding the next three methods, subclasses can change the way
|
||
that const pointers (like SEL, Class, Atomic strings, etc) are
|
||
archived. */
|
||
|
||
- (unsigned) _coderCreateReferenceForConstPtr: (const void*)ptr
|
||
{
|
||
unsigned xref;
|
||
if (!const_ptr_2_xref)
|
||
const_ptr_2_xref =
|
||
NSCreateMapTable (NSNonOwnedPointerOrNullMapKeyCallBacks,
|
||
NSIntMapValueCallBacks, 0);
|
||
|
||
xref = NSCountMapTable (const_ptr_2_xref) + 1;
|
||
assert (! NSMapGet (const_ptr_2_xref, (void*)xref));
|
||
NSMapInsert (const_ptr_2_xref, ptr, (void*)xref);
|
||
return xref;
|
||
}
|
||
|
||
- (unsigned) _coderReferenceForConstPtr: (const void*)ptr
|
||
{
|
||
if (const_ptr_2_xref)
|
||
return (unsigned) NSMapGet (const_ptr_2_xref, ptr);
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Methods for forward references */
|
||
|
||
- (unsigned) _coderCreateForwardReferenceForObject: anObject
|
||
{
|
||
unsigned fref;
|
||
if (!object_2_fref)
|
||
object_2_fref =
|
||
NSCreateMapTable (NSNonOwnedPointerOrNullMapKeyCallBacks,
|
||
NSIntMapValueCallBacks, 0);
|
||
fref = ++fref_counter;
|
||
assert ( ! NSMapGet (object_2_fref, anObject));
|
||
NSMapInsert (object_2_fref, anObject, (void*)fref);
|
||
return fref;
|
||
}
|
||
|
||
- (unsigned) _coderForwardReferenceForObject: anObject
|
||
{
|
||
/* This method must return 0 if it's not there. */
|
||
if (!object_2_fref)
|
||
return 0;
|
||
return (unsigned) NSMapGet (object_2_fref, anObject);
|
||
}
|
||
|
||
- (void) _coderRemoveForwardReferenceForObject: anObject
|
||
{
|
||
NSMapRemove (object_2_fref, anObject);
|
||
}
|
||
|
||
|
||
/* This is the Coder's interface to the over-ridable
|
||
"_coderPutObject:atReference" method. Do not override it. It
|
||
handles the root_object_table. */
|
||
|
||
- (void) _coderInternalCreateReferenceForObject: anObj
|
||
{
|
||
[self _coderCreateReferenceForObject: anObj];
|
||
}
|
||
|
||
|
||
/* Method for encoding things. */
|
||
|
||
- (void) encodeValueOfCType: (const char*)type
|
||
at: (const void*)d
|
||
withName: (id <String>)name
|
||
{
|
||
[cstream encodeValueOfCType:type
|
||
at:d
|
||
withName:name];
|
||
}
|
||
|
||
- (void) encodeBytes: (const void *)b
|
||
count: (unsigned)c
|
||
withName: (id <String>)name
|
||
{
|
||
/* xxx Is this what we want?
|
||
It won't be cleanly readable in TextCStream's. */
|
||
[cstream encodeName: name];
|
||
[[cstream stream] writeBytes: b length: c];
|
||
}
|
||
|
||
|
||
- (void) encodeTag: (unsigned char)t
|
||
{
|
||
if ([cstream respondsToSelector: @selector(encodeTag:)])
|
||
[(id)cstream encodeTag:t];
|
||
else
|
||
[self encodeValueOfCType:@encode(unsigned char)
|
||
at:&t
|
||
withName:@"Coder tag"];
|
||
}
|
||
|
||
- (void) encodeClass: aClass
|
||
{
|
||
[self encodeIndent];
|
||
if (aClass == Nil)
|
||
{
|
||
[self encodeTag: CODER_CLASS_NIL];
|
||
}
|
||
else
|
||
{
|
||
/* xxx Perhaps I should do classname substitution here. */
|
||
const char *class_name = class_get_class_name (aClass);
|
||
unsigned xref;
|
||
|
||
/* Do classname substitution, ala encodeClassName:intoClassName */
|
||
if (classname_2_classname)
|
||
{
|
||
char *subst_class_name = NSMapGet (classname_2_classname,
|
||
class_name);
|
||
if (subst_class_name)
|
||
{
|
||
class_name = subst_class_name;
|
||
aClass = objc_lookup_class (class_name);
|
||
}
|
||
}
|
||
|
||
xref = [self _coderReferenceForConstPtr: aClass];
|
||
if (xref)
|
||
{
|
||
/* It's already been encoded, so just encode the x-reference */
|
||
[self encodeTag: CODER_CLASS_REPEATED];
|
||
[self encodeValueOfCType: @encode(unsigned)
|
||
at: &xref
|
||
withName: @"Class cross-reference number"];
|
||
}
|
||
else
|
||
{
|
||
/* It hasn't been encoded before; encode it. */
|
||
int class_version = class_get_version (aClass);
|
||
|
||
assert (class_name);
|
||
assert (*class_name);
|
||
|
||
[self encodeTag: CODER_CLASS];
|
||
[self encodeValueOfCType: @encode(char*)
|
||
at: &class_name
|
||
withName: @"Class name"];
|
||
[self encodeValueOfCType: @encode(int)
|
||
at: &class_version
|
||
withName: @"Class version"];
|
||
[self _coderCreateReferenceForConstPtr: aClass];
|
||
}
|
||
}
|
||
[self encodeUnindent];
|
||
return;
|
||
}
|
||
|
||
- (void) encodeAtomicString: (const char*) sp
|
||
withName: (id <String>) name
|
||
{
|
||
/* xxx Add repeat-string-ptr checking here. */
|
||
[self notImplemented:_cmd];
|
||
[self encodeValueOfCType:@encode(char*) at:&sp withName:name];
|
||
}
|
||
|
||
- (void) encodeSelector: (SEL)sel withName: (id <String>) name
|
||
{
|
||
[self encodeName:name];
|
||
[self encodeIndent];
|
||
if (sel == 0)
|
||
{
|
||
[self encodeTag: CODER_CONST_PTR_NULL];
|
||
}
|
||
else
|
||
{
|
||
unsigned xref = [self _coderReferenceForConstPtr: sel];
|
||
if (xref)
|
||
{
|
||
/* It's already been encoded, so just encode the x-reference */
|
||
[self encodeTag: CODER_CONST_PTR_REPEATED];
|
||
[self encodeValueOfCType: @encode(unsigned)
|
||
at: &xref
|
||
withName: @"SEL cross-reference number"];
|
||
}
|
||
else
|
||
{
|
||
const char *sel_name;
|
||
const char *sel_types;
|
||
|
||
[self encodeTag: CODER_CONST_PTR];
|
||
|
||
/* Get the selector name and type. */
|
||
sel_name = sel_get_name(sel);
|
||
#if NeXT_runtime
|
||
sel_types = NO_SEL_TYPES;
|
||
#else
|
||
sel_types = sel_get_type(sel);
|
||
#endif
|
||
#if 1 /* xxx Yipes,... careful... */
|
||
/* xxx Think about something like this. */
|
||
if (!sel_types)
|
||
sel_types =
|
||
sel_get_type (sel_get_any_typed_uid (sel_get_name (sel)));
|
||
#endif
|
||
if (!sel_name || !*sel_name)
|
||
[NSException raise: NSGenericException
|
||
format: @"ObjC runtime didn't provide SEL name"];
|
||
if (!sel_types || !*sel_types)
|
||
[NSException raise: NSGenericException
|
||
format: @"ObjC runtime didn't provide SEL type"];
|
||
|
||
[self _coderCreateReferenceForConstPtr: sel];
|
||
[self encodeValueOfCType: @encode(char*)
|
||
at: &sel_name
|
||
withName: @"SEL name"];
|
||
[self encodeValueOfCType: @encode(char*)
|
||
at: &sel_types
|
||
withName: @"SEL types"];
|
||
if (debug_coder)
|
||
fprintf(stderr, "Coder encoding registered sel xref %u\n", xref);
|
||
}
|
||
}
|
||
[self encodeUnindent];
|
||
return;
|
||
}
|
||
|
||
- (void) encodeValueOfObjCType: (const char*) type
|
||
at: (const void*) d
|
||
withName: (id <String>) name
|
||
{
|
||
switch (*type)
|
||
{
|
||
case _C_CLASS:
|
||
[self encodeName: name];
|
||
[self encodeClass: *(id*)d];
|
||
break;
|
||
case _C_ATOM:
|
||
[self encodeAtomicString: *(char**)d withName: name];
|
||
break;
|
||
case _C_SEL:
|
||
{
|
||
[self encodeSelector: *(SEL*)d withName: name];
|
||
break;
|
||
}
|
||
case _C_ID:
|
||
[self encodeObject: *(id*)d withName: name];
|
||
break;
|
||
default:
|
||
[self encodeValueOfCType:type at:d withName:name];
|
||
}
|
||
}
|
||
|
||
|
||
/* Methods for handling interconnected objects */
|
||
|
||
- (void) startEncodingInterconnectedObjects
|
||
{
|
||
interconnect_stack_height++;
|
||
}
|
||
|
||
- (void) finishEncodingInterconnectedObjects
|
||
{
|
||
/* xxx Perhaps we should look at the forward references and
|
||
encode here any forward-referenced objects that haven't been
|
||
encoded yet. No---the current behavior implements NeXT's
|
||
-encodeConditionalObject: */
|
||
assert (interconnect_stack_height);
|
||
interconnect_stack_height--;
|
||
}
|
||
|
||
/* NOTE: Unlike NeXT's, this *can* be called recursively */
|
||
- (void) encodeRootObject: anObj
|
||
withName: (id <String>)name
|
||
{
|
||
[self encodeName: @"Root Object"];
|
||
[self encodeIndent];
|
||
[self encodeTag: CODER_OBJECT_ROOT];
|
||
[self startEncodingInterconnectedObjects];
|
||
[self encodeObject: anObj withName: name];
|
||
[self finishEncodingInterconnectedObjects];
|
||
[self encodeUnindent];
|
||
}
|
||
|
||
|
||
/* These next two methods are the designated coder methods called when
|
||
we've determined that the object has not already been
|
||
encoded---we're not simply going to encode a cross-reference number
|
||
to the object, we're actually going to encode an object (either a
|
||
proxy to the object or the object itself).
|
||
|
||
ConnectedCoder overrides _doEncodeObject: in order to implement
|
||
the encoding of proxies. */
|
||
|
||
- (void) _doEncodeBycopyObject: anObj
|
||
{
|
||
id encoded_object, encoded_class;
|
||
|
||
/* Give the user the opportunity to substitute the class and object */
|
||
/* xxx Is this the right place for this substitution? */
|
||
if ([[self class] isKindOf: [NSCoder class]]
|
||
&& ! [[self class] isKindOf: [NSArchiver class]])
|
||
/* Make sure we don't do this for the Coder class, because
|
||
by default Coder should behave like NSArchiver. */
|
||
{
|
||
encoded_object = [anObj replacementObjectForCoder: (NSCoder*)self];
|
||
encoded_class = [encoded_object classForCoder];
|
||
}
|
||
else
|
||
{
|
||
encoded_object = [anObj replacementObjectForArchiver: (NSArchiver*)self];
|
||
encoded_class = [encoded_object classForArchiver];
|
||
}
|
||
[self encodeClass: encoded_class];
|
||
/* xxx We should make sure it responds to this selector! */
|
||
[encoded_object encodeWithCoder: (id)self];
|
||
}
|
||
|
||
/* This method overridden by ConnectedCoder */
|
||
- (void) _doEncodeObject: anObj
|
||
{
|
||
[self _doEncodeBycopyObject:anObj];
|
||
}
|
||
|
||
|
||
/* This is the designated object encoder */
|
||
- (void) _encodeObject: anObj
|
||
withName: (id <String>) name
|
||
isBycopy: (BOOL) bycopy_flag
|
||
isForwardReference: (BOOL) forward_ref_flag
|
||
{
|
||
[self encodeName:name];
|
||
[self encodeIndent];
|
||
if (!anObj)
|
||
{
|
||
[self encodeTag:CODER_OBJECT_NIL];
|
||
}
|
||
else if (my_object_is_class(anObj))
|
||
{
|
||
[self encodeTag: CODER_OBJECT_CLASS];
|
||
[self encodeClass:anObj];
|
||
}
|
||
else
|
||
{
|
||
unsigned xref = [self _coderReferenceForObject: anObj];
|
||
if (xref)
|
||
{
|
||
/* It's already been encoded, so just encode the x-reference */
|
||
[self encodeTag: CODER_OBJECT_REPEATED];
|
||
[self encodeValueOfCType: @encode(unsigned)
|
||
at: &xref
|
||
withName: @"Object cross-reference number"];
|
||
}
|
||
else if (forward_ref_flag
|
||
|| (in_progress_table
|
||
&& NSMapGet (in_progress_table, anObj)))
|
||
{
|
||
unsigned fref;
|
||
|
||
/* We are going to encode a forward reference, either because
|
||
(1) our caller asked for it, or (2) we are in the middle
|
||
of encoding this object, and haven't finished encoding it yet. */
|
||
/* Find out if it already has a forward reference number. */
|
||
fref = [self _coderForwardReferenceForObject: anObj];
|
||
if (!fref)
|
||
/* It doesn't, so create one. */
|
||
fref = [self _coderCreateForwardReferenceForObject: anObj];
|
||
[self encodeTag: CODER_OBJECT_FORWARD_REFERENCE];
|
||
[self encodeValueOfCType: @encode(unsigned)
|
||
at: &fref
|
||
withName: @"Object forward cross-reference number"];
|
||
}
|
||
else
|
||
{
|
||
/* No backward or forward references, we are going to encode
|
||
the object. */
|
||
unsigned fref;
|
||
|
||
/* Register the object as being in progress of encoding. */
|
||
if (!in_progress_table)
|
||
in_progress_table =
|
||
/* This is "NonOwnedPointer", and not "Object", because
|
||
with "Object" we would get an infinite loop with distributed
|
||
objects when we try to put a Proxy in in the table, and
|
||
send the proxy the -hash method. */
|
||
NSCreateMapTable (NSNonOwnedPointerMapKeyCallBacks,
|
||
NSIntMapValueCallBacks, 0);
|
||
NSMapInsert (in_progress_table, anObj, (void*)1);
|
||
|
||
/* Encode the object. */
|
||
[self encodeTag: CODER_OBJECT];
|
||
[self encodeIndent];
|
||
if (bycopy_flag)
|
||
[self _doEncodeBycopyObject:anObj];
|
||
else
|
||
[self _doEncodeObject:anObj];
|
||
[self encodeUnindent];
|
||
|
||
/* Find out if this object satisfies any forward references,
|
||
and encode either the forward reference number, or a
|
||
zero. NOTE: This test is here, and not before the
|
||
_doEncode.., because the encoding of this object may,
|
||
itself, generate a "forward reference" to this object,
|
||
(ala the in_progress_table). That is, we cannot know
|
||
whether this object satisfies a forward reference until
|
||
after it has been encoded. */
|
||
fref = [self _coderForwardReferenceForObject: anObj];
|
||
if (fref)
|
||
{
|
||
/* It does satisfy a forward reference; write the forward
|
||
reference number, so the decoder can know. */
|
||
[self encodeValueOfCType: @encode(unsigned)
|
||
at: &fref
|
||
withName: @"Object forward cross-reference number"];
|
||
/* Remove it from the forward reference table, since we'll never
|
||
have another forward reference for this object. */
|
||
[self _coderRemoveForwardReferenceForObject: anObj];
|
||
}
|
||
else
|
||
{
|
||
/* It does not satisfy any forward references. Let the
|
||
decoder know this by encoding NULL. Note: in future
|
||
encoding we may have backward references to this
|
||
object, but we will never need forward references to
|
||
this object. */
|
||
unsigned null_fref = 0;
|
||
[self encodeValueOfCType: @encode(unsigned)
|
||
at: &null_fref
|
||
withName: @"Object forward cross-reference number"];
|
||
}
|
||
|
||
/* Register that we have encoded it so that future encoding can
|
||
do backward references properly. */
|
||
[self _coderInternalCreateReferenceForObject: anObj];
|
||
|
||
/* We're done encoding the object, it's no longer in progress. */
|
||
NSMapRemove (in_progress_table, anObj);
|
||
}
|
||
}
|
||
[self encodeUnindent];
|
||
}
|
||
|
||
- (void) encodeObject: anObj
|
||
withName: (id <String>)name
|
||
{
|
||
[self _encodeObject:anObj withName:name isBycopy:NO isForwardReference:NO];
|
||
}
|
||
|
||
|
||
- (void) encodeBycopyObject: anObj
|
||
withName: (id <String>)name
|
||
{
|
||
[self _encodeObject:anObj withName:name isBycopy:YES isForwardReference:NO];
|
||
}
|
||
|
||
- (void) encodeObjectReference: anObj
|
||
withName: (id <String>)name
|
||
{
|
||
[self _encodeObject:anObj withName:name isBycopy:NO isForwardReference:YES];
|
||
}
|
||
|
||
|
||
|
||
- (void) encodeWithName: (id <String>)name
|
||
valuesOfObjCTypes: (const char *)types, ...
|
||
{
|
||
va_list ap;
|
||
|
||
[self encodeName:name];
|
||
va_start(ap, types);
|
||
while (*types)
|
||
{
|
||
[self encodeValueOfObjCType:types
|
||
at:va_arg(ap, void*)
|
||
withName:@"Encoded Types Component"];
|
||
types = objc_skip_typespec(types);
|
||
}
|
||
va_end(ap);
|
||
}
|
||
|
||
- (void) encodeValueOfObjCTypes: (const char *)types
|
||
at: (const void *)d
|
||
withName: (id <String>)name
|
||
{
|
||
[self encodeName:name];
|
||
while (*types)
|
||
{
|
||
[self encodeValueOfObjCType:types
|
||
at:d
|
||
withName:@"Encoded Types Component"];
|
||
types = objc_skip_typespec(types);
|
||
}
|
||
}
|
||
|
||
- (void) encodeArrayOfObjCType: (const char *)type
|
||
count: (unsigned)c
|
||
at: (const void *)d
|
||
withName: (id <String>)name
|
||
{
|
||
int i;
|
||
int offset = objc_sizeof_type(type);
|
||
const char *where = d;
|
||
|
||
[self encodeName:name];
|
||
for (i = 0; i < c; i++)
|
||
{
|
||
[self encodeValueOfObjCType:type
|
||
at:where
|
||
withName:@"Encoded Array Component"];
|
||
where += offset;
|
||
}
|
||
}
|
||
|
||
- (void) encodeIndent
|
||
{
|
||
[cstream encodeIndent];
|
||
}
|
||
|
||
- (void) encodeUnindent
|
||
{
|
||
[cstream encodeUnindent];
|
||
}
|
||
|
||
- (void) encodeName: (id <String>)n
|
||
{
|
||
[cstream encodeName: n];
|
||
}
|
||
|
||
|
||
/* Substituting Classes */
|
||
|
||
- (id <String>) classNameEncodedForTrueClassName: (id <String>) trueName
|
||
{
|
||
[self notImplemented: _cmd];
|
||
return nil;
|
||
|
||
#if 0
|
||
if (classname_2_classname)
|
||
return NSMapGet (classname_2_classname, [trueName cStringNoCopy]);
|
||
return trueName;
|
||
#endif
|
||
}
|
||
|
||
- (void) encodeClassName: (id <String>) trueName
|
||
intoClassName: (id <String>) inArchiveName
|
||
{
|
||
[self notImplemented: _cmd];
|
||
|
||
#if 0
|
||
/* The table should hold char*'s, not id's. */
|
||
if (!classname_2_classname)
|
||
classname_2_classname =
|
||
NSCreateMapTable (NSObjectsMapKeyCallBacks,
|
||
NSObjectsMapValueCallBacks, 0);
|
||
NSMapInsert (classname_2_classname, trueName, inArchiveName);
|
||
#endif
|
||
}
|
||
|
||
- (void) dealloc
|
||
{
|
||
if (in_progress_table) NSFreeMapTable (in_progress_table);
|
||
if (object_2_xref) NSFreeMapTable (object_2_xref);
|
||
if (object_2_fref) NSFreeMapTable (object_2_fref);
|
||
if (const_ptr_2_xref) NSFreeMapTable (const_ptr_2_xref);
|
||
if (classname_2_classname) NSFreeMapTable (classname_2_classname);
|
||
[super dealloc];
|
||
}
|
||
|
||
@end
|