mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-25 17:51:01 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1521 72102866-910b-0410-8b05-ffd578937521
854 lines
23 KiB
Objective-C
854 lines
23 KiB
Objective-C
/* Abstract class for writing objects to a stream
|
||
Copyright (C) 1996 Free Software Foundation, Inc.
|
||
|
||
Written by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
|
||
Created: February 1996, with core from Coder, created 1994.
|
||
|
||
This file is part of the GNUstep Base 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 <gnustep/base/preface.h>
|
||
#include <gnustep/base/Coder.h>
|
||
#include <gnustep/base/CoderPrivate.h>
|
||
#include <gnustep/base/MemoryStream.h>
|
||
#include <gnustep/base/StdioStream.h>
|
||
#include <gnustep/base/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;
|
||
|
||
static int 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];
|
||
}
|
||
|
||
|
||
/* Handling the in_progress_table. These are called before and after
|
||
the actual object (not a forward or backward reference) is encoded.
|
||
|
||
One of these objects should also call
|
||
-_coderInternalCreateReferenceForObject:. GNU archiving calls it
|
||
in the first, in order to force forward references to objects that
|
||
are in progress; this allows for -initWithCoder: methods that
|
||
deallocate self, and return another object. OpenStep-style coding
|
||
calls it in the second, meaning that we never create forward
|
||
references to objects that are in progress; we encode a backward
|
||
reference to the in progress object, and assume that it will not
|
||
change location. */
|
||
|
||
- (void) _objectWillBeInProgress: anObj
|
||
{
|
||
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);
|
||
}
|
||
|
||
- (void) _objectNoLongerInProgress: anObj
|
||
{
|
||
NSMapRemove (in_progress_table, anObj);
|
||
/* Register that we have encoded it so that future encoding can
|
||
do backward references properly. */
|
||
[self _coderInternalCreateReferenceForObject: 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. In
|
||
OpenStep-style archiving, this method also calls
|
||
-_coderInternalCreateReferenceForObject:. */
|
||
[self _objectWillBeInProgress: anObj];
|
||
|
||
/* 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"];
|
||
}
|
||
|
||
/* We're done encoding the object, it's no longer in progress.
|
||
In GNU-style archiving, this method also calls
|
||
-_coderInternalCreateReferenceForObject:. */
|
||
[self _objectNoLongerInProgress: 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
|