ftp.nice.ch/Attic/openStep/implementation/gnustep/sources/gstep-base-0.2.7.tgz#/gstep-base-0.2.7/src/Decoder.m

This is Decoder.m in view mode; [Download] [Up]

/* Abstract class for reading objects from 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/CStream.h>
#include <gnustep/base/Stream.h>
#include <gnustep/base/StdioStream.h>
#include <gnustep/base/Array.h>
#include <Foundation/NSException.h>

static int debug_coder = 0;

@implementation Decoder


/* Signature Handling. */

+ (void) readSignatureFromCStream: (id <CStreaming>) cs
		     getClassname: (char *) name
		    formatVersion: (int*) version
{
  int got;
  char package_name[64];
  int major_version;
  int minor_version;
  int subminor_version;

  got = [[cs stream] readFormat: SIGNATURE_FORMAT_STRING,
		     &package_name, 
		     &major_version,
		     &minor_version,
		     &subminor_version,
		     name, version];
  if (got != 6)
    [NSException raise: CoderSignatureMalformedException
		 format: @"Decoder found a malformed signature"];
}

/* This is the designated initializer. */
+ newReadingFromStream: (id <Streaming>) stream
{
  id cs = [CStream cStreamReadingFromStream: stream];
  char name[128];		/* Max classname length. */
  int version;
  Decoder *new_coder;

  [self readSignatureFromCStream: cs
	getClassname: name
	formatVersion: &version];

  new_coder = [[objc_lookup_class(name) alloc]
		_initWithCStream: cs
		formatVersion: version];
  new_coder->xref_2_object = NULL;
  new_coder->xref_2_object_root = NULL;
  new_coder->fref_2_object = NULL;
  new_coder->address_2_fref = NULL;
  new_coder->zone = NSDefaultMallocZone();
  return new_coder;
}

+ newReadingFromFile: (id <String>) filename
{
  return [self newReadingFromStream: 
		 [StdioStream streamWithFilename: filename 
			      fmode: "r"]];
}

+ decodeObjectWithName: (id <String> *) name
	    fromStream: (id <Streaming>)stream;
{
  id c, o;
  c = [self newReadingFromStream:stream];
  [c decodeObjectAt: &o withName: name];
  [c release];
  return [o autorelease];
}

+ decodeObjectWithName: (id <String> *) name
	      fromFile: (id <String>) filename;
{
  return [self decodeObjectWithName: name
	       fromStream:
		 [StdioStream streamWithFilename:filename fmode: "r"]];
}



/* Functions and methods for keeping cross-references
   so objects that were already read can be referred to again. */

/* These _coder... methods may be overriden by subclasses so that 
   cross-references can be kept differently. */

- (unsigned) _coderCreateReferenceForObject: anObj
{
  if (!xref_2_object)
    {
      xref_2_object = [Array new];
      /* Append an object so our xref numbers are in sync with the 
	 Encoders, which start at 1. */
      [xref_2_object appendObject: [NSObject new]];
    }
  if (debug_coder)
    fprintf (stderr, "Decoder registering object xref %u\n",
	     [xref_2_object count] - 1);
  [xref_2_object appendObject: anObj]; // xxx but this will retain anObj.  NO.
  /* This return value should be the same as the index of anObj 
     in xref_2_object. */
  return ([xref_2_object count] - 1);
}

- _coderObjectAtReference: (unsigned)xref
{
  assert (xref_2_object);
  return [xref_2_object objectAtIndex: xref];
}


/* The methods for the root object table */

- (void) _coderPushRootObjectTable
{
  if (!xref_2_object_root)
    xref_2_object_root = [Array new];
}

- (void) _coderPopRootObjectTable
{
  assert (xref_2_object_root);
  if (!interconnect_stack_height)
    {
      [xref_2_object_root release];
      xref_2_object_root = NULL;
    }
}

- (unsigned) _coderCreateReferenceForInterconnectedObject: anObj
{
  if (!xref_2_object_root)
    {
      xref_2_object_root = [Array new];
      /* Append an object so our xref numbers are in sync with the 
	 Encoders, which start at 1. */
      [xref_2_object_root appendObject: [[NSObject new] autorelease]];
    }
  [xref_2_object_root appendObject: anObj];
  /* This return value should be the same as the index of anObj 
     in xref_2_object_root. */
  return ([xref_2_object_root count] - 1);
}

- _coderTopRootObjectTable
{
  assert (xref_2_object_root);
  return xref_2_object_root;
}


/* Using 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 (!xref_2_const_ptr)
    {
      xref_2_const_ptr = NSCreateMapTable (NSIntMapKeyCallBacks,
					   NSNonOwnedPointerMapValueCallBacks,
					   0);
      /* Append an object so our xref numbers are in sync with the 
	 Encoders, which start at 1. */
      NSMapInsert (xref_2_const_ptr, (void*)0, (void*)1);
    }
  xref = NSCountMapTable (xref_2_const_ptr);
  NSMapInsert (xref_2_const_ptr, (void*)xref, ptr);
  return xref;
}

- (const void*) _coderConstPtrAtReference: (unsigned)xref;
{
  assert (xref_2_const_ptr);
  return NSMapGet (xref_2_const_ptr, (void*)xref);
}


/* Here are the methods for forward object references. */

- (void) _coderPushForwardObjectTable
{
#if 0
  if (!fref_stack)
    fref_stack = o_list_of_void_p ();
  o_list_append_element (fref_stack, NSCreateMap (...));
#endif
  if (!address_2_fref)
    address_2_fref = NSCreateMapTable (NSNonOwnedPointerMapKeyCallBacks,
				       NSIntMapValueCallBacks, 0);
				    
}

- (void) _coderPopForwardObjectTable
{
  assert (address_2_fref);
  if (!interconnect_stack_height)
    {
      NSFreeMapTable (address_2_fref);
      address_2_fref = NULL;
    }
}

- (void) _coderSatisfyForwardReference: (unsigned)fref withObject: anObj
{
  assert (address_2_fref);
  if (!fref_2_object)
    /* xxx Or should this be NSObjectMapValueCallBacks, so we make
       sure the object doesn't get released before we can resolve
       references with it? */
    fref_2_object = NSCreateMapTable (NSIntMapKeyCallBacks,
				      NSNonOwnedPointerMapValueCallBacks, 0);
  /* There should only be one object for each fref. */
  assert (!NSMapGet (fref_2_object, (void*)fref));
  NSMapInsert (fref_2_object, (void*)fref, anObj);
}

- (void) _coderAssociateForwardReference: (unsigned)fref
		       withObjectAddress: (void*)addr
{
  /* Register ADDR as associated with FREF; later we will put id 
     associated with FREF at ADDR. */
  assert (address_2_fref);
  /* There should not be duplicate addresses */
  assert (!NSMapGet (address_2_fref, addr));
  NSMapInsert (address_2_fref, addr, (void*)fref);
}

- (void) _coderResolveTopForwardReferences
{
  /* Enumerate the forward references and put them at the proper addresses. */
  NSMapEnumerator me;
  void *fref;
  void *addr;

  if (!address_2_fref)
    return;

  /* Go through all the addresses that are needing to be filled
     in with forward references, and put the correct object there.
     If fref_2_object does not contain an object for fref, (i.e. there 
     was no satisfier for the forward reference), put nil there. */
  me = NSEnumerateMapTable (address_2_fref);
  while (NSNextMapEnumeratorPair (&me, &addr, &fref))
    *(id*)addr = (id) NSMapGet (fref_2_object, fref);
}


/* This is the Coder's interface to the over-ridable
   "_coderCreateReferenceForObject" method.  Do not override it.  It
   handles the xref_2_object_root. */

- (unsigned) _coderInternalCreateReferenceForObject: anObj
{
  unsigned xref = [self _coderCreateReferenceForObject: anObj];
  if (DOING_ROOT_OBJECT)
    [self _coderCreateReferenceForInterconnectedObject: anObj];
  return xref;
}



/* Method for decoding things. */

- (void) decodeValueOfCType: (const char*)type
   at: (void*)d 
   withName: (id <String> *)namePtr
{
  [cstream decodeValueOfCType:type
	   at:d
	   withName:namePtr];
}

- (void) decodeBytes: (void *)b
   count: (unsigned)c
   withName: (id <String> *) name
{
  int actual_count;
  /* xxx Is this what we want?  
     It won't be cleanly readable in TextCStream's. */
  [cstream decodeName: name];
  actual_count = [[cstream stream] readBytes: b length: c];
  assert (actual_count == c);
}

- (unsigned char) decodeTag
{
  if ([cstream respondsToSelector: @selector(decodeTag)])
    return [(id)cstream decodeTag];
  {
    unsigned char t;
    [self decodeValueOfCType:@encode(unsigned char)
	  at:&t 
	  withName:NULL];
    return t;
  }
}

- decodeClass
{
  unsigned char tag;
  char *class_name;
  int class_version;
  id ret = Nil;
  
  [self decodeIndent];
  tag = [self decodeTag];
  switch (tag)
    {
    case CODER_CLASS_NIL:
      break;
    case CODER_CLASS_REPEATED:
      {
	unsigned xref;
	[self decodeValueOfCType: @encode(unsigned)
	      at: &xref
	      withName: NULL];
	ret = (id) [self _coderConstPtrAtReference: xref];
	if (!ret)
	  [NSException 
	    raise: NSGenericException
	    format: @"repeated class cross-reference number %u not found",
	    xref];
	break;
      }
    case CODER_CLASS:
      {
	[self decodeValueOfCType: @encode(char*)
	      at: &class_name
	      withName: NULL];
	[self decodeValueOfCType: @encode(int)
	      at: &class_version
	      withName: NULL];

	/* xxx should do classname substitution, 
	   ala decodeClassName:intoClassName: here. */

	ret = objc_lookup_class (class_name);
	if (ret == Nil)
	  [NSException raise: NSGenericException
		       format: @"Couldn't find class `%s'", class_name];
	if (class_get_version(ret) != class_version)
	  [NSException 
	    raise: NSGenericException
	    format: @"Class version mismatch, executable %d != encoded %d",
	    class_get_version(ret), class_version];

	{
	  unsigned xref;
	  xref = [self _coderCreateReferenceForConstPtr: ret];
	  if (debug_coder)
	    fprintf(stderr,
		    "Decoder decoding registered class xref %u\n", xref);
	}
	(*objc_free) (class_name);
	break;
      }
    default:
      [NSException raise: NSGenericException
		   format: @"unrecognized class tag = %d", (int)tag];
    }
  [self decodeUnindent];
  return ret;
}

- (const char *) decodeAtomicStringWithName: (id <String> *) name
{
  char *s;
  /* xxx Add repeat-string-ptr checking here */
  [self notImplemented:_cmd];
  [self decodeValueOfCType:@encode(char*) at:&s withName:name];
  return s;
}

- (SEL) decodeSelectorWithName: (id <String> *) name
{
  char tag;
  SEL ret = NULL;

  [self decodeName:name];
  [self decodeIndent];
  tag = [self decodeTag];
  switch (tag)
    {
    case CODER_CONST_PTR_NULL:
      break;
    case CODER_CONST_PTR_REPEATED:
      {
	unsigned xref;
	[self decodeValueOfCType: @encode(unsigned)
	      at: &xref
	      withName: NULL];
	ret = (SEL) [self _coderConstPtrAtReference: xref];
	if (!ret)
	  [NSException 
	    raise: NSGenericException
	    format: @"repeated selector cross-reference number %u not found",
		xref];
	break;
      }
    case CODER_CONST_PTR:
      {
	char *sel_name;
	char *sel_types;

	[self decodeValueOfCType:@encode(char *) 
	      at:&sel_name 
	      withName:NULL];
	[self decodeValueOfCType:@encode(char *) 
	      at:&sel_types 
	      withName:NULL];
#if NeXT_runtime
	ret = sel_getUid(sel_name);
#else
	if (!strcmp(sel_types, NO_SEL_TYPES))
	  ret = sel_get_any_uid(sel_name);
	else
	  ret = sel_get_typed_uid(sel_name, sel_types);
#endif
	if (!ret)
	  [NSException raise: NSGenericException
		       format: @"Could not find selector (%s) with types [%s]",
		       sel_name, sel_types];
#if ! NeXT_runtime
	if (strcmp(sel_types, NO_SEL_TYPES)
	    && !(sel_types_match(sel_types, ret->sel_types)))
	  [NSException 
	    raise: NSGenericException
	    format: @"ObjC runtime didn't provide SEL with matching type"];
#endif
	{
	  unsigned xref;
	  xref = [self _coderCreateReferenceForConstPtr: ret];
	  if (debug_coder)
	    fprintf(stderr,
		    "Decoder decoding registered sel xref %u\n", xref);
	}
	(*objc_free)(sel_name);
	(*objc_free)(sel_types);
	break;
      }
    default:
      [NSException raise: NSGenericException
		   format: @"unrecognized selector tag = %d", (int)tag];
    }
  [self decodeUnindent];
  return ret;
}


- (void) startDecodingInterconnectedObjects
{
  interconnect_stack_height++;
  [self _coderPushRootObjectTable];
  [self _coderPushForwardObjectTable];
}

- (void) finishDecodingInterconnectedObjects
{
#if 0
  SEL awake_sel = sel_get_any_uid("awakeAfterUsingCoder:");
#endif
  
  assert (interconnect_stack_height);

  /* xxx This might not be the right thing to do; perhaps we should do
     this finishing up work at the end of each nested call, not just
     at the end of all nested calls.
     However, then we might miss some forward references that we could
     have resolved otherwise. */
  if (--interconnect_stack_height)
    return;

  /* xxx fix the use of _coderPopForwardObjectTable and
     _coderPopRootObjectTable. */

  /* resolve object forward references */
  [self _coderResolveTopForwardReferences];
  [self _coderPopForwardObjectTable];

#if 0
  When should this be done? 
  /* send "-awakeAfterUsingCoder:" to all the objects that were read */
  /* xxx But this doesn't currently handle the return of a different 
     object than was messaged! */
  if (awake_sel)
    {
      void ask_awake(elt e)
	{
	  if (__objc_responds_to(e.id_u, awake_sel))
	    (*objc_msg_lookup(e.id_u,awake_sel))(e.id_u, awake_sel, self);
	}
      [[self _coderTopRootObjectTable] withElementsCall:ask_awake];
    }
#endif
  [self _coderPopRootObjectTable];
}

- (void) _decodeRootObjectAt: (id*)ret withName: (id <String> *) name
{
  [self startDecodingInterconnectedObjects];
  [self decodeObjectAt:ret withName:name];
  [self finishDecodingInterconnectedObjects];
}


- (void) decodeValueOfObjCType: (const char*)type
   at: (void*)d 
   withName: (id <String> *)namePtr
{
  switch (*type)
    {
    case _C_CLASS:
      {
	[self decodeName:namePtr];
	*(id*)d = [self decodeClass];
	break;
      }
    case _C_ATOM:
      *(const char**)d = [self decodeAtomicStringWithName:namePtr];
      break;
    case _C_SEL:
      *(SEL*)d = [self decodeSelectorWithName:namePtr];
      break;
    case _C_ID:
      [self decodeObjectAt:d withName:namePtr];
      break;
    default:
      [self decodeValueOfCType:type at:d withName:namePtr];
    }
  /* xxx We need to catch unions and make a sensible error message */
}

- (BOOL) _createReferenceBeforeInit
{
  return NO;
}

/* This is the designated (and one-and-only) object decoder */
- (void) decodeObjectAt: (id*) anObjPtr withName: (id <String> *) name
{
  unsigned char tag;
  unsigned fref = 0;
  id dummy_object;

  /* Sometimes the user wants to decode an object, but doesn't care to
     have a pointer to it, (LinkedList elements, for example).  In
     this case, the user can pass in NULL for anObjPtr, and all will
     be safe. */
  if (!anObjPtr)
    anObjPtr = &dummy_object;

  [self decodeName:name];
  [self decodeIndent];
  tag = [self decodeTag];
  switch (tag)
    {
    case CODER_OBJECT_NIL:
      *anObjPtr = nil;
      break;
    case CODER_OBJECT_CLASS:
      *anObjPtr = [self decodeClass];
      break;
    case CODER_OBJECT_FORWARD_REFERENCE:
      {
	if (!DOING_ROOT_OBJECT)
	  [NSException 
	    raise: NSGenericException
	    format: @"can't decode forward reference when not decoding "
	    @"a root object"];
	[self decodeValueOfCType: @encode(unsigned)
	      at: &fref 
	      withName: NULL];
	/* The user doesn't need the object pointer anyway, don't record
	   it in the table. */
	if (anObjPtr == &dummy_object)
	  break;
	[self _coderAssociateForwardReference: fref
	      withObjectAddress: anObjPtr];
	break;
      }
    case CODER_OBJECT:
      {
	Class object_class;
	SEL new_sel = sel_get_any_uid ("newWithCoder:");
	Method* new_method;
	BOOL create_ref_before_init = [self _createReferenceBeforeInit];

	[self decodeIndent];
	object_class = [self decodeClass];
	/* xxx Should change the runtime.
	   class_get_class_method should take the class as its first
	   argument, not the metaclass! */
	new_method = class_get_class_method(class_get_meta_class(object_class),
					    new_sel);
	if (new_method && !create_ref_before_init)
	  *anObjPtr = (*(new_method->method_imp))(object_class, new_sel, self);
	else
	  {
	    SEL init_sel = sel_get_any_uid("initWithCoder:");
	    Method *init_method = 
	      class_get_instance_method(object_class, init_sel);
	    /* xxx Or should I send +alloc? */
	    *anObjPtr = (id) NSAllocateObject (object_class, 0, zone);
	    if (create_ref_before_init)
	      [self _coderInternalCreateReferenceForObject: *anObjPtr];
	    if (init_method)
	      *anObjPtr = 
		(*(init_method->method_imp))(*anObjPtr, init_sel, self);
	    /* xxx else what, error? */
	  }
	/* xxx Should I sent -awakeUsingCoder: here instead of above? */
	[self decodeUnindent];

	/* If this was a CODER_OBJECT_FORWARD_SATISFIER, then remember it. */
	[self decodeValueOfCType: @encode(unsigned)
	      at: &fref 
	      withName: NULL];
	if (fref)
	  {
	    NSAssert (!create_ref_before_init,
		      @"You are trying to decode an object with the non-GNU\n"
		      @"OpenStep-style forward references, but the object's\n"
		      @"decoding mechanism wants to use GNU features.");
	    [self _coderSatisfyForwardReference: fref withObject: *anObjPtr];
	  }

	/* Would get error here with Connection-wide object references
	   because addProxy gets called in +newRemote:connection: */
	if (!create_ref_before_init)
	  {
	    unsigned xref = 
	      [self _coderInternalCreateReferenceForObject: *anObjPtr];
	    if (debug_coder)
	      fprintf(stderr, 
		      "Decoder decoding registered class xref %u\n", xref);
	  }
	break;
      }
    case CODER_OBJECT_ROOT:
      {
	[self _decodeRootObjectAt: anObjPtr withName: name];
	break;
      }
    case CODER_OBJECT_REPEATED:
      {
	unsigned xref;

	[self decodeValueOfCType: @encode(unsigned)
	      at: &xref 
	      withName: NULL];
	*anObjPtr = [self _coderObjectAtReference: xref];
	if (!*anObjPtr)
	  [NSException 
	    raise: NSGenericException
	    format: @"repeated object cross-reference number %u not found",
	    xref];
	break;
      }
    default:
      [NSException raise: NSGenericException
		   format: @"unrecognized object tag = %d", (int)tag];
    }
  [self decodeUnindent];
}


- (void) decodeWithName: (id <String> *)name
   valuesOfObjCTypes: (const char *)types, ...
{
  va_list ap;

  [self decodeName:name];
  va_start(ap, types);
  while (*types)
    {
      [self decodeValueOfObjCType:types
	    at:va_arg(ap, void*)
	    withName:NULL];
      types = objc_skip_typespec(types);
    }
  va_end(ap);
}

- (void) decodeValueOfObjCTypes: (const char *)types
   at: (void *)d
   withName: (id <String> *)name
{
  [self decodeName:name];
  while (*types)
    {
      [self decodeValueOfObjCType:types
	    at:d
	    withName:NULL];
      types = objc_skip_typespec(types);
    }
}

- (void) decodeArrayOfObjCType: (const char *)type
   count: (unsigned)c
   at: (void *)d
   withName: (id <String> *) name
{
  int i;
  int offset = objc_sizeof_type(type);
  char *where = d;

  [self decodeName:name];
  for (i = 0; i < c; i++)
    {
      [self decodeValueOfObjCType:type
	    at:where
	    withName:NULL];
      where += offset;
    }
}

- (void) decodeIndent
{
  [cstream decodeIndent];
}

- (void) decodeUnindent
{
  [cstream decodeUnindent];
}

- (void) decodeName: (id <String> *)n
{
  [cstream decodeName: n];
}


+ (NSString*) classNameDecodedForArchiveClassName: (NSString*) inArchiveName
{
  [self notImplemented:_cmd];
  return nil;
}

+ (void) decodeClassName: (NSString*) inArchiveName
             asClassName:(NSString *)trueName
{
  [self notImplemented:_cmd];
}


/* Managing Zones */

- (NSZone*) objectZone
{
  return zone;
}

- (void) setObjectZone: (NSZone*)z
{
  zone = z;
}

- (void) dealloc
{
  if (xref_2_object) [xref_2_object release];
  if (xref_2_object_root) [xref_2_object_root release];
  if (xref_2_const_ptr) NSFreeMapTable (xref_2_const_ptr);
  if (fref_2_object) NSFreeMapTable (fref_2_object);
  if (address_2_fref) NSFreeMapTable (address_2_fref);
  [super dealloc];
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.