diff options
Diffstat (limited to 'libobjc/archive.c')
-rw-r--r-- | libobjc/archive.c | 1656 |
1 files changed, 1656 insertions, 0 deletions
diff --git a/libobjc/archive.c b/libobjc/archive.c new file mode 100644 index 000000000..f691b13b6 --- /dev/null +++ b/libobjc/archive.c @@ -0,0 +1,1656 @@ + /* GNU Objective C Runtime archiving + Copyright (C) 1993, 1995, 1996, 1997, 2002, 2004, 2009, + 2010 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3, or (at your option) any later version. + +GCC 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 General Public License for more +details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +/* This file is entirely deprecated and will be removed. */ + +#include "objc-private/common.h" +#include "objc-private/error.h" +#include "tconfig.h" +#include "objc/objc-api.h" +#include "objc/hash.h" +#include "objc/objc-list.h" +#include "objc-private/runtime.h" +#include "objc/typedstream.h" +#include "objc/encoding.h" +#include <stdlib.h> + +extern int fflush (FILE *); + +#define ROUND(V, A) \ + ({ typeof (V) __v = (V); typeof (A) __a = (A); \ + __a * ((__v + __a - 1)/__a); }) + +#define PTR2LONG(P) (((char *) (P))-(char *) 0) +#define LONG2PTR(L) (((char *) 0) + (L)) + +/* Declare some functions... */ + +static int +objc_read_class (struct objc_typed_stream *stream, Class *class); + +int objc_sizeof_type (const char *type); + +static int +objc_write_use_common (struct objc_typed_stream *stream, unsigned long key); + +static int +objc_write_register_common (struct objc_typed_stream *stream, + unsigned long key); + +static int +objc_write_class (struct objc_typed_stream *stream, + struct objc_class *class); + +const char *objc_skip_type (const char *type); + +static void __objc_finish_write_root_object (struct objc_typed_stream *); +static void __objc_finish_read_root_object (struct objc_typed_stream *); + +static inline int +__objc_code_unsigned_char (unsigned char *buf, unsigned char val) +{ + if ((val&_B_VALUE) == val) + { + buf[0] = val|_B_SINT; + return 1; + } + else + { + buf[0] = _B_NINT|0x01; + buf[1] = val; + return 2; + } +} + +int +objc_write_unsigned_char (struct objc_typed_stream *stream, + unsigned char value) +{ + unsigned char buf[sizeof (unsigned char) + 1]; + int len = __objc_code_unsigned_char (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + +static inline int +__objc_code_char (unsigned char *buf, signed char val) +{ + if (val >= 0) + return __objc_code_unsigned_char (buf, val); + else + { + buf[0] = _B_NINT|_B_SIGN|0x01; + buf[1] = -val; + return 2; + } +} + +int +objc_write_char (struct objc_typed_stream *stream, signed char value) +{ + unsigned char buf[sizeof (char) + 1]; + int len = __objc_code_char (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + +static inline int +__objc_code_unsigned_short (unsigned char *buf, unsigned short val) +{ + if ((val&_B_VALUE) == val) + { + buf[0] = val|_B_SINT; + return 1; + } + else + { + int c, b; + + buf[0] = _B_NINT; + + for (c = sizeof (short); c != 0; c -= 1) + if (((val >> (8*(c - 1)))%0x100) != 0) + break; + + buf[0] |= c; + + for (b = 1; c != 0; c--, b++) + { + buf[b] = (val >> (8*(c - 1)))%0x100; + } + + return b; + } +} + +int +objc_write_unsigned_short (struct objc_typed_stream *stream, + unsigned short value) +{ + unsigned char buf[sizeof (unsigned short) + 1]; + int len = __objc_code_unsigned_short (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + +static inline int +__objc_code_short (unsigned char *buf, short val) +{ + int sign = (val < 0); + int size = __objc_code_unsigned_short (buf, sign ? -val : val); + if (sign) + buf[0] |= _B_SIGN; + return size; +} + +int +objc_write_short (struct objc_typed_stream *stream, short value) +{ + unsigned char buf[sizeof (short) + 1]; + int len = __objc_code_short (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + + +static inline int +__objc_code_unsigned_int (unsigned char *buf, unsigned int val) +{ + if ((val&_B_VALUE) == val) + { + buf[0] = val|_B_SINT; + return 1; + } + else + { + int c, b; + + buf[0] = _B_NINT; + + for (c = sizeof (int); c != 0; c -= 1) + if (((val >> (8*(c - 1)))%0x100) != 0) + break; + + buf[0] |= c; + + for (b = 1; c != 0; c--, b++) + { + buf[b] = (val >> (8*(c-1)))%0x100; + } + + return b; + } +} + +int +objc_write_unsigned_int (struct objc_typed_stream *stream, unsigned int value) +{ + unsigned char buf[sizeof (unsigned int) + 1]; + int len = __objc_code_unsigned_int (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + +static inline int +__objc_code_int (unsigned char *buf, int val) +{ + int sign = (val < 0); + int size = __objc_code_unsigned_int (buf, sign ? -val : val); + if (sign) + buf[0] |= _B_SIGN; + return size; +} + +int +objc_write_int (struct objc_typed_stream *stream, int value) +{ + unsigned char buf[sizeof (int) + 1]; + int len = __objc_code_int (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + +static inline int +__objc_code_unsigned_long (unsigned char *buf, unsigned long val) +{ + if ((val&_B_VALUE) == val) + { + buf[0] = val|_B_SINT; + return 1; + } + else + { + int c, b; + + buf[0] = _B_NINT; + + for (c = sizeof (long); c != 0; c -= 1) + if (((val >> (8*(c - 1)))%0x100) != 0) + break; + + buf[0] |= c; + + for (b = 1; c != 0; c--, b++) + { + buf[b] = (val >> (8*(c - 1)))%0x100; + } + + return b; + } +} + +int +objc_write_unsigned_long (struct objc_typed_stream *stream, + unsigned long value) +{ + unsigned char buf[sizeof (unsigned long) + 1]; + int len = __objc_code_unsigned_long (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + +static inline int +__objc_code_long (unsigned char *buf, long val) +{ + int sign = (val < 0); + int size = __objc_code_unsigned_long (buf, sign ? -val : val); + if (sign) + buf[0] |= _B_SIGN; + return size; +} + +int +objc_write_long (struct objc_typed_stream *stream, long value) +{ + unsigned char buf[sizeof (long) + 1]; + int len = __objc_code_long (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + + +int +objc_write_string (struct objc_typed_stream *stream, + const unsigned char *string, unsigned int nbytes) +{ + unsigned char buf[sizeof (unsigned int) + 1]; + int len = __objc_code_unsigned_int (buf, nbytes); + + if ((buf[0]&_B_CODE) == _B_SINT) + buf[0] = (buf[0]&_B_VALUE)|_B_SSTR; + + else /* _B_NINT */ + buf[0] = (buf[0]&_B_VALUE)|_B_NSTR; + + if ((*stream->write) (stream->physical, (char*)buf, len) != 0) + return (*stream->write) (stream->physical, (char*)string, nbytes); + else + return 0; +} + +int +objc_write_string_atomic (struct objc_typed_stream *stream, + unsigned char *string, unsigned int nbytes) +{ + unsigned long key; + if ((key = PTR2LONG(objc_hash_value_for_key (stream->stream_table, string)))) + return objc_write_use_common (stream, key); + else + { + int length; + objc_hash_add (&stream->stream_table, + LONG2PTR(key=PTR2LONG(string)), string); + if ((length = objc_write_register_common (stream, key))) + return objc_write_string (stream, string, nbytes); + return length; + } +} + +static int +objc_write_register_common (struct objc_typed_stream *stream, + unsigned long key) +{ + unsigned char buf[sizeof (unsigned long)+2]; + int len = __objc_code_unsigned_long (buf + 1, key); + if (len == 1) + { + buf[0] = _B_RCOMM|0x01; + buf[1] &= _B_VALUE; + return (*stream->write) (stream->physical, (char*)buf, len + 1); + } + else + { + buf[1] = (buf[1]&_B_VALUE)|_B_RCOMM; + return (*stream->write) (stream->physical, (char*)buf + 1, len); + } +} + +static int +objc_write_use_common (struct objc_typed_stream *stream, unsigned long key) +{ + unsigned char buf[sizeof (unsigned long)+2]; + int len = __objc_code_unsigned_long (buf + 1, key); + if (len == 1) + { + buf[0] = _B_UCOMM|0x01; + buf[1] &= _B_VALUE; + return (*stream->write) (stream->physical, (char*)buf, 2); + } + else + { + buf[1] = (buf[1]&_B_VALUE)|_B_UCOMM; + return (*stream->write) (stream->physical, (char*)buf + 1, len); + } +} + +static inline int +__objc_write_extension (struct objc_typed_stream *stream, unsigned char code) +{ + if (code <= _B_VALUE) + { + unsigned char buf = code|_B_EXT; + return (*stream->write) (stream->physical, (char*)&buf, 1); + } + else + { + _objc_abort ("__objc_write_extension: bad opcode %c\n", code); + return -1; + } +} + +int +__objc_write_object (struct objc_typed_stream *stream, id object) +{ + unsigned char buf = '\0'; + SEL write_sel = sel_get_any_uid ("write:"); + if (object) + { + __objc_write_extension (stream, _BX_OBJECT); + objc_write_class (stream, object->class_pointer); + (*objc_msg_lookup (object, write_sel)) (object, write_sel, stream); + return (*stream->write) (stream->physical, (char*)&buf, 1); + } + else + return objc_write_use_common (stream, 0); +} + +int +objc_write_object_reference (struct objc_typed_stream *stream, id object) +{ + unsigned long key; + if ((key = PTR2LONG(objc_hash_value_for_key (stream->object_table, object)))) + return objc_write_use_common (stream, key); + + __objc_write_extension (stream, _BX_OBJREF); + return objc_write_unsigned_long (stream, PTR2LONG (object)); +} + +int +objc_write_root_object (struct objc_typed_stream *stream, id object) +{ + int len = 0; + if (stream->writing_root_p) + _objc_abort ("objc_write_root_object called recursively"); + else + { + stream->writing_root_p = 1; + __objc_write_extension (stream, _BX_OBJROOT); + if ((len = objc_write_object (stream, object))) + __objc_finish_write_root_object (stream); + stream->writing_root_p = 0; + } + return len; +} + +int +objc_write_object (struct objc_typed_stream *stream, id object) +{ + unsigned long key; + if ((key = PTR2LONG(objc_hash_value_for_key (stream->object_table, object)))) + return objc_write_use_common (stream, key); + + else if (object == nil) + return objc_write_use_common (stream, 0); + + else + { + int length; + objc_hash_add (&stream->object_table, + LONG2PTR(key=PTR2LONG(object)), object); + if ((length = objc_write_register_common (stream, key))) + return __objc_write_object (stream, object); + return length; + } +} + +int +__objc_write_class (struct objc_typed_stream *stream, struct objc_class *class) +{ + __objc_write_extension (stream, _BX_CLASS); + objc_write_string_atomic (stream, (unsigned char *) class->name, + strlen ((char *) class->name)); + return objc_write_unsigned_long (stream, class->version); +} + + +static int +objc_write_class (struct objc_typed_stream *stream, + struct objc_class *class) +{ + unsigned long key; + if ((key = PTR2LONG(objc_hash_value_for_key (stream->stream_table, class)))) + return objc_write_use_common (stream, key); + else + { + int length; + objc_hash_add (&stream->stream_table, + LONG2PTR(key = PTR2LONG(class)), class); + if ((length = objc_write_register_common (stream, key))) + return __objc_write_class (stream, class); + return length; + } +} + + +int +__objc_write_selector (struct objc_typed_stream *stream, SEL selector) +{ + const char *sel_name; + __objc_write_extension (stream, _BX_SEL); + /* to handle NULL selectors */ + if ((SEL)0 == selector) + return objc_write_string (stream, (unsigned char*)"", 0); + sel_name = sel_get_name (selector); + return objc_write_string (stream, (unsigned char*)sel_name, strlen ((char*)sel_name)); +} + +int +objc_write_selector (struct objc_typed_stream *stream, SEL selector) +{ + const char *sel_name; + unsigned long key; + + /* to handle NULL selectors */ + if ((SEL)0 == selector) + return __objc_write_selector (stream, selector); + + sel_name = sel_get_name (selector); + if ((key = PTR2LONG(objc_hash_value_for_key (stream->stream_table, + sel_name)))) + return objc_write_use_common (stream, key); + else + { + int length; + objc_hash_add (&stream->stream_table, + LONG2PTR(key = PTR2LONG(sel_name)), (char *) sel_name); + if ((length = objc_write_register_common (stream, key))) + return __objc_write_selector (stream, selector); + return length; + } +} + + + +/* +** Read operations +*/ + +int +objc_read_char (struct objc_typed_stream *stream, char *val) +{ + unsigned char buf; + int len; + len = (*stream->read) (stream->physical, (char*)&buf, 1); + if (len != 0) + { + if ((buf & _B_CODE) == _B_SINT) + (*val) = (buf & _B_VALUE); + + else if ((buf & _B_NUMBER) == 1) + { + len = (*stream->read) (stream->physical, val, 1); + if (buf&_B_SIGN) + (*val) = -1 * (*val); + } + + else + _objc_abort ("expected 8bit signed int, got %dbit int", + (int) (buf&_B_NUMBER)*8); + } + return len; +} + + +int +objc_read_unsigned_char (struct objc_typed_stream *stream, unsigned char *val) +{ + unsigned char buf; + int len; + if ((len = (*stream->read) (stream->physical, (char*)&buf, 1))) + { + if ((buf & _B_CODE) == _B_SINT) + (*val) = (buf & _B_VALUE); + + else if ((buf & _B_NUMBER) == 1) + len = (*stream->read) (stream->physical, (char*)val, 1); + + else + _objc_abort ("expected 8bit unsigned int, got %dbit int", + (int) (buf&_B_NUMBER)*8); + } + return len; +} + +int +objc_read_short (struct objc_typed_stream *stream, short *value) +{ + unsigned char buf[sizeof (short) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + if ((buf[0] & _B_CODE) == _B_SINT) + (*value) = (buf[0] & _B_VALUE); + + else + { + int pos = 1; + int nbytes = buf[0] & _B_NUMBER; + if (nbytes > (int) sizeof (short)) + _objc_abort ("expected short, got bigger (%dbits)", nbytes*8); + len = (*stream->read) (stream->physical, (char*)buf + 1, nbytes); + (*value) = 0; + while (pos <= nbytes) + (*value) = ((*value)*0x100) + buf[pos++]; + if (buf[0] & _B_SIGN) + (*value) = -(*value); + } + } + return len; +} + +int +objc_read_unsigned_short (struct objc_typed_stream *stream, + unsigned short *value) +{ + unsigned char buf[sizeof (unsigned short) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + if ((buf[0] & _B_CODE) == _B_SINT) + (*value) = (buf[0] & _B_VALUE); + + else + { + int pos = 1; + int nbytes = buf[0] & _B_NUMBER; + if (nbytes > (int) sizeof (short)) + _objc_abort ("expected short, got int or bigger"); + len = (*stream->read) (stream->physical, (char*)buf + 1, nbytes); + (*value) = 0; + while (pos <= nbytes) + (*value) = ((*value)*0x100) + buf[pos++]; + } + } + return len; +} + + +int +objc_read_int (struct objc_typed_stream *stream, int *value) +{ + unsigned char buf[sizeof (int) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + if ((buf[0] & _B_CODE) == _B_SINT) + (*value) = (buf[0] & _B_VALUE); + + else + { + int pos = 1; + int nbytes = buf[0] & _B_NUMBER; + if (nbytes > (int) sizeof (int)) + _objc_abort ("expected int, got bigger"); + len = (*stream->read) (stream->physical, (char*)buf + 1, nbytes); + (*value) = 0; + while (pos <= nbytes) + (*value) = ((*value)*0x100) + buf[pos++]; + if (buf[0] & _B_SIGN) + (*value) = -(*value); + } + } + return len; +} + +int +objc_read_long (struct objc_typed_stream *stream, long *value) +{ + unsigned char buf[sizeof (long) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + if ((buf[0] & _B_CODE) == _B_SINT) + (*value) = (buf[0] & _B_VALUE); + + else + { + int pos = 1; + int nbytes = buf[0] & _B_NUMBER; + if (nbytes > (int) sizeof (long)) + _objc_abort ("expected long, got bigger"); + len = (*stream->read) (stream->physical, (char*)buf + 1, nbytes); + (*value) = 0; + while (pos <= nbytes) + (*value) = ((*value)*0x100) + buf[pos++]; + if (buf[0] & _B_SIGN) + (*value) = -(*value); + } + } + return len; +} + +int +__objc_read_nbyte_uint (struct objc_typed_stream *stream, + unsigned int nbytes, unsigned int *val) +{ + int len; + unsigned int pos = 0; + unsigned char buf[sizeof (unsigned int) + 1]; + + if (nbytes > sizeof (int)) + _objc_abort ("expected int, got bigger"); + + len = (*stream->read) (stream->physical, (char*)buf, nbytes); + (*val) = 0; + while (pos < nbytes) + (*val) = ((*val)*0x100) + buf[pos++]; + return len; +} + + +int +objc_read_unsigned_int (struct objc_typed_stream *stream, + unsigned int *value) +{ + unsigned char buf[sizeof (unsigned int) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + if ((buf[0] & _B_CODE) == _B_SINT) + (*value) = (buf[0] & _B_VALUE); + + else + len = __objc_read_nbyte_uint (stream, (buf[0] & _B_VALUE), value); + + } + return len; +} + +int +__objc_read_nbyte_ulong (struct objc_typed_stream *stream, + unsigned int nbytes, unsigned long *val) +{ + int len; + unsigned int pos = 0; + unsigned char buf[sizeof (unsigned long) + 1]; + + if (nbytes > sizeof (long)) + _objc_abort ("expected long, got bigger"); + + len = (*stream->read) (stream->physical, (char*)buf, nbytes); + (*val) = 0; + while (pos < nbytes) + (*val) = ((*val)*0x100) + buf[pos++]; + return len; +} + + +int +objc_read_unsigned_long (struct objc_typed_stream *stream, + unsigned long *value) +{ + unsigned char buf[sizeof (unsigned long) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + if ((buf[0] & _B_CODE) == _B_SINT) + (*value) = (buf[0] & _B_VALUE); + + else + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), value); + + } + return len; +} + +int +objc_read_string (struct objc_typed_stream *stream, + char **string) +{ + unsigned char buf[sizeof (unsigned int) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + unsigned long key = 0; + + if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */ + { + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + len = (*stream->read) (stream->physical, (char*)buf, 1); + } + + switch (buf[0]&_B_CODE) { + case _B_SSTR: + { + int length = buf[0]&_B_VALUE; + (*string) = (char*)objc_malloc (length + 1); + if (key) + objc_hash_add (&stream->stream_table, LONG2PTR(key), *string); + len = (*stream->read) (stream->physical, *string, length); + (*string)[length] = '\0'; + } + break; + + case _B_UCOMM: + { + char *tmp; + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + tmp = objc_hash_value_for_key (stream->stream_table, LONG2PTR (key)); + *string = objc_malloc (strlen (tmp) + 1); + strcpy (*string, tmp); + } + break; + + case _B_NSTR: + { + unsigned int nbytes = buf[0]&_B_VALUE; + len = __objc_read_nbyte_uint (stream, nbytes, &nbytes); + if (len) { + (*string) = (char*)objc_malloc (nbytes + 1); + if (key) + objc_hash_add (&stream->stream_table, LONG2PTR(key), *string); + len = (*stream->read) (stream->physical, *string, nbytes); + (*string)[nbytes] = '\0'; + } + } + break; + + default: + _objc_abort ("expected string, got opcode %c\n", (buf[0]&_B_CODE)); + } + } + + return len; +} + + +int +objc_read_object (struct objc_typed_stream *stream, id *object) +{ + unsigned char buf[sizeof (unsigned int)]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + SEL read_sel = sel_get_any_uid ("read:"); + unsigned long key = 0; + + if ((buf[0]&_B_CODE) == _B_RCOMM) /* register common */ + { + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + len = (*stream->read) (stream->physical, (char*)buf, 1); + } + + if (buf[0] == (_B_EXT | _BX_OBJECT)) + { + Class class; + + /* get class */ + len = objc_read_class (stream, &class); + + /* create instance */ + (*object) = class_create_instance (class); + + /* register? */ + if (key) + objc_hash_add (&stream->object_table, LONG2PTR(key), *object); + + /* send -read: */ + if (__objc_responds_to (*object, read_sel)) + (*get_imp (class, read_sel)) (*object, read_sel, stream); + + /* check null-byte */ + len = (*stream->read) (stream->physical, (char*)buf, 1); + if (buf[0] != '\0') + _objc_abort ("expected null-byte, got opcode %c", buf[0]); + } + + else if ((buf[0]&_B_CODE) == _B_UCOMM) + { + if (key) + _objc_abort ("cannot register use upcode..."); + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + (*object) = objc_hash_value_for_key (stream->object_table, + LONG2PTR(key)); + } + + else if (buf[0] == (_B_EXT | _BX_OBJREF)) /* a forward reference */ + { + struct objc_list *other; + len = objc_read_unsigned_long (stream, &key); + other + = (struct objc_list *) objc_hash_value_for_key (stream->object_refs, + LONG2PTR(key)); + objc_hash_add (&stream->object_refs, LONG2PTR(key), + (void *)list_cons (object, other)); + } + + else if (buf[0] == (_B_EXT | _BX_OBJROOT)) /* a root object */ + { + if (key) + _objc_abort ("cannot register root object..."); + len = objc_read_object (stream, object); + __objc_finish_read_root_object (stream); + } + + else + _objc_abort ("expected object, got opcode %c", buf[0]); + } + return len; +} + +static int +objc_read_class (struct objc_typed_stream *stream, Class *class) +{ + unsigned char buf[sizeof (unsigned int)]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + unsigned long key = 0; + + if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */ + { + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + len = (*stream->read) (stream->physical, (char*)buf, 1); + } + + if (buf[0] == (_B_EXT | _BX_CLASS)) + { + char temp[1] = ""; + char *class_name = temp; + unsigned long version; + + /* get class */ + len = objc_read_string (stream, &class_name); + (*class) = objc_get_class (class_name); + objc_free (class_name); + + /* register */ + if (key) + objc_hash_add (&stream->stream_table, LONG2PTR(key), *class); + + objc_read_unsigned_long (stream, &version); + objc_hash_add (&stream->class_table, + (*class)->name, (void *) ((size_t) version)); + } + + else if ((buf[0]&_B_CODE) == _B_UCOMM) + { + if (key) + _objc_abort ("cannot register use upcode..."); + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + *class = objc_hash_value_for_key (stream->stream_table, + LONG2PTR(key)); + if (! *class) + _objc_abort ("cannot find class for key %lu", key); + } + + else + _objc_abort ("expected class, got opcode %c", buf[0]); + } + return len; +} + +int +objc_read_selector (struct objc_typed_stream *stream, SEL* selector) +{ + unsigned char buf[sizeof (unsigned int)]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + unsigned long key = 0; + + if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */ + { + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + len = (*stream->read) (stream->physical, (char*)buf, 1); + } + + if (buf[0] == (_B_EXT|_BX_SEL)) /* selector! */ + { + char temp[1] = ""; + char *selector_name = temp; + + /* get selector */ + len = objc_read_string (stream, &selector_name); + /* To handle NULL selectors */ + if (0 == strlen (selector_name)) + { + (*selector) = (SEL)0; + return 0; + } + else + (*selector) = sel_get_any_uid (selector_name); + objc_free (selector_name); + + /* register */ + if (key) + objc_hash_add (&stream->stream_table, + LONG2PTR(key), (void *) *selector); + } + + else if ((buf[0]&_B_CODE) == _B_UCOMM) + { + if (key) + _objc_abort ("cannot register use upcode..."); + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + (*selector) = objc_hash_value_for_key (stream->stream_table, + LONG2PTR(key)); + } + + else + _objc_abort ("expected selector, got opcode %c", buf[0]); + } + return len; +} + +/* +** USER LEVEL FUNCTIONS +*/ + +/* +** Write one object, encoded in TYPE and pointed to by DATA to the +** typed stream STREAM. +*/ + +int +objc_write_type (TypedStream *stream, const char *type, const void *data) +{ + switch (*type) { + case _C_ID: + return objc_write_object (stream, *(id *) data); + break; + + case _C_CLASS: + return objc_write_class (stream, *(Class *) data); + break; + + case _C_SEL: + return objc_write_selector (stream, *(SEL *) data); + break; + + case _C_CHR: + return objc_write_char (stream, *(signed char *) data); + break; + + case _C_UCHR: + return objc_write_unsigned_char (stream, *(unsigned char *) data); + break; + + case _C_SHT: + return objc_write_short (stream, *(short *) data); + break; + + case _C_USHT: + return objc_write_unsigned_short (stream, *(unsigned short *) data); + break; + + case _C_INT: + return objc_write_int (stream, *(int *) data); + break; + + case _C_UINT: + return objc_write_unsigned_int (stream, *(unsigned int *) data); + break; + + case _C_LNG: + return objc_write_long (stream, *(long *) data); + break; + + case _C_ULNG: + return objc_write_unsigned_long (stream, *(unsigned long *) data); + break; + + case _C_CHARPTR: + return objc_write_string (stream, + *(unsigned char **) data, strlen (*(char **) data)); + break; + + case _C_ATOM: + return objc_write_string_atomic (stream, *(unsigned char **) data, + strlen (*(char **) data)); + break; + + case _C_ARY_B: + { + int len = atoi (type + 1); + while (isdigit ((unsigned char) *++type)) + ; + return objc_write_array (stream, type, len, data); + } + break; + + case _C_STRUCT_B: + { + int acc_size = 0; + int align; + while (*type != _C_STRUCT_E && *type++ != '=') + ; /* skip "<name>=" */ + while (*type != _C_STRUCT_E) + { + align = objc_alignof_type (type); /* padd to alignment */ + acc_size = ROUND (acc_size, align); + objc_write_type (stream, type, ((char *) data) + acc_size); + acc_size += objc_sizeof_type (type); /* add component size */ + type = objc_skip_typespec (type); /* skip component */ + } + return 1; + } + + default: + { + _objc_abort ("objc_write_type: cannot parse typespec: %s\n", type); + return 0; + } + } +} + +/* +** Read one object, encoded in TYPE and pointed to by DATA to the +** typed stream STREAM. DATA specifies the address of the types to +** read. Expected type is checked against the type actually present +** on the stream. +*/ + +int +objc_read_type(TypedStream *stream, const char *type, void *data) +{ + char c; + switch (c = *type) { + case _C_ID: + return objc_read_object (stream, (id*)data); + break; + + case _C_CLASS: + return objc_read_class (stream, (Class*)data); + break; + + case _C_SEL: + return objc_read_selector (stream, (SEL*)data); + break; + + case _C_CHR: + return objc_read_char (stream, (char*)data); + break; + + case _C_UCHR: + return objc_read_unsigned_char (stream, (unsigned char*)data); + break; + + case _C_SHT: + return objc_read_short (stream, (short*)data); + break; + + case _C_USHT: + return objc_read_unsigned_short (stream, (unsigned short*)data); + break; + + case _C_INT: + return objc_read_int (stream, (int*)data); + break; + + case _C_UINT: + return objc_read_unsigned_int (stream, (unsigned int*)data); + break; + + case _C_LNG: + return objc_read_long (stream, (long*)data); + break; + + case _C_ULNG: + return objc_read_unsigned_long (stream, (unsigned long*)data); + break; + + case _C_CHARPTR: + case _C_ATOM: + return objc_read_string (stream, (char**)data); + break; + + case _C_ARY_B: + { + int len = atoi (type + 1); + while (isdigit ((unsigned char) *++type)) + ; + return objc_read_array (stream, type, len, data); + } + break; + + case _C_STRUCT_B: + { + int acc_size = 0; + int align; + while (*type != _C_STRUCT_E && *type++ != '=') + ; /* skip "<name>=" */ + while (*type != _C_STRUCT_E) + { + align = objc_alignof_type (type); /* padd to alignment */ + acc_size = ROUND (acc_size, align); + objc_read_type (stream, type, ((char*)data)+acc_size); + acc_size += objc_sizeof_type (type); /* add component size */ + type = objc_skip_typespec (type); /* skip component */ + } + return 1; + } + + default: + { + _objc_abort ("objc_read_type: cannot parse typespec: %s\n", type); + return 0; + } + } +} + +/* +** Write the object specified by the template TYPE to STREAM. Last +** arguments specify addresses of values to be written. It might +** seem surprising to specify values by address, but this is extremely +** convenient for copy-paste with objc_read_types calls. A more +** down-to-the-earth cause for this passing of addresses is that values +** of arbitrary size is not well supported in ANSI C for functions with +** variable number of arguments. +*/ + +int +objc_write_types (TypedStream *stream, const char *type, ...) +{ + va_list args; + const char *c; + int res = 0; + + va_start(args, type); + + for (c = type; *c; c = objc_skip_typespec (c)) + { + switch (*c) { + case _C_ID: + res = objc_write_object (stream, *va_arg (args, id*)); + break; + + case _C_CLASS: + res = objc_write_class (stream, *va_arg (args, Class*)); + break; + + case _C_SEL: + res = objc_write_selector (stream, *va_arg (args, SEL*)); + break; + + case _C_CHR: + res = objc_write_char (stream, *va_arg (args, char*)); + break; + + case _C_UCHR: + res = objc_write_unsigned_char (stream, + *va_arg (args, unsigned char*)); + break; + + case _C_SHT: + res = objc_write_short (stream, *va_arg (args, short*)); + break; + + case _C_USHT: + res = objc_write_unsigned_short (stream, + *va_arg (args, unsigned short*)); + break; + + case _C_INT: + res = objc_write_int(stream, *va_arg (args, int*)); + break; + + case _C_UINT: + res = objc_write_unsigned_int(stream, *va_arg (args, unsigned int*)); + break; + + case _C_LNG: + res = objc_write_long(stream, *va_arg (args, long*)); + break; + + case _C_ULNG: + res = objc_write_unsigned_long(stream, *va_arg (args, unsigned long*)); + break; + + case _C_CHARPTR: + { + unsigned char **str = va_arg (args, unsigned char **); + res = objc_write_string (stream, *str, strlen ((char*)*str)); + } + break; + + case _C_ATOM: + { + unsigned char **str = va_arg (args, unsigned char **); + res = objc_write_string_atomic (stream, *str, strlen ((char*)*str)); + } + break; + + case _C_ARY_B: + { + int len = atoi (c + 1); + const char *t = c; + while (isdigit ((unsigned char) *++t)) + ; + res = objc_write_array (stream, t, len, va_arg (args, void *)); + t = objc_skip_typespec (t); + if (*t != _C_ARY_E) + _objc_abort ("expected `]', got: %s", t); + } + break; + + default: + _objc_abort ("objc_write_types: cannot parse typespec: %s\n", type); + } + } + va_end(args); + return res; +} + + +/* +** Last arguments specify addresses of values to be read. Expected +** type is checked against the type actually present on the stream. +*/ + +int +objc_read_types(TypedStream *stream, const char *type, ...) +{ + va_list args; + const char *c; + int res = 0; + + va_start (args, type); + + for (c = type; *c; c = objc_skip_typespec(c)) + { + switch (*c) { + case _C_ID: + res = objc_read_object(stream, va_arg (args, id*)); + break; + + case _C_CLASS: + res = objc_read_class(stream, va_arg (args, Class*)); + break; + + case _C_SEL: + res = objc_read_selector(stream, va_arg (args, SEL*)); + break; + + case _C_CHR: + res = objc_read_char(stream, va_arg (args, char*)); + break; + + case _C_UCHR: + res = objc_read_unsigned_char(stream, va_arg (args, unsigned char*)); + break; + + case _C_SHT: + res = objc_read_short(stream, va_arg (args, short*)); + break; + + case _C_USHT: + res = objc_read_unsigned_short(stream, va_arg (args, unsigned short*)); + break; + + case _C_INT: + res = objc_read_int(stream, va_arg (args, int*)); + break; + + case _C_UINT: + res = objc_read_unsigned_int(stream, va_arg (args, unsigned int*)); + break; + + case _C_LNG: + res = objc_read_long(stream, va_arg (args, long*)); + break; + + case _C_ULNG: + res = objc_read_unsigned_long(stream, va_arg (args, unsigned long*)); + break; + + case _C_CHARPTR: + case _C_ATOM: + { + char **str = va_arg (args, char **); + res = objc_read_string (stream, str); + } + break; + + case _C_ARY_B: + { + int len = atoi (c + 1); + const char *t = c; + while (isdigit ((unsigned char) *++t)) + ; + res = objc_read_array (stream, t, len, va_arg (args, void *)); + t = objc_skip_typespec (t); + if (*t != _C_ARY_E) + _objc_abort ("expected `]', got: %s", t); + } + break; + + default: + _objc_abort ("objc_read_types: cannot parse typespec: %s\n", type); + } + } + va_end (args); + return res; +} + +/* +** Write an array of COUNT elements of TYPE from the memory address DATA. +** This is equivalent of objc_write_type (stream, "[N<type>]", data) +*/ + +int +objc_write_array (TypedStream *stream, const char *type, + int count, const void *data) +{ + int off = objc_sizeof_type(type); + const char *where = data; + + while (count-- > 0) + { + objc_write_type(stream, type, where); + where += off; + } + return 1; +} + +/* +** Read an array of COUNT elements of TYPE into the memory address +** DATA. The memory pointed to by data is supposed to be allocated +** by the callee. This is equivalent of +** objc_read_type (stream, "[N<type>]", data) +*/ + +int +objc_read_array (TypedStream *stream, const char *type, + int count, void *data) +{ + int off = objc_sizeof_type(type); + char *where = (char*)data; + + while (count-- > 0) + { + objc_read_type(stream, type, where); + where += off; + } + return 1; +} + +static int +__objc_fread (FILE *file, char *data, int len) +{ + return fread(data, len, 1, file); +} + +static int +__objc_fwrite (FILE *file, char *data, int len) +{ + return fwrite(data, len, 1, file); +} + +static int +__objc_feof (FILE *file) +{ + return feof(file); +} + +static int +__objc_no_write (FILE *file __attribute__ ((__unused__)), + const char *data __attribute__ ((__unused__)), + int len __attribute__ ((__unused__))) +{ + _objc_abort ("TypedStream not open for writing"); + return 0; +} + +static int +__objc_no_read (FILE *file __attribute__ ((__unused__)), + const char *data __attribute__ ((__unused__)), + int len __attribute__ ((__unused__))) +{ + _objc_abort ("TypedStream not open for reading"); + return 0; +} + +static int +__objc_read_typed_stream_signature (TypedStream *stream) +{ + char buffer[80]; + int pos = 0; + do + (*stream->read) (stream->physical, buffer+pos, 1); + while (buffer[pos++] != '\0') + ; + sscanf (buffer, "GNU TypedStream %d", &stream->version); + if (stream->version != OBJC_TYPED_STREAM_VERSION) + _objc_abort ("cannot handle TypedStream version %d", stream->version); + return 1; +} + +static int +__objc_write_typed_stream_signature (TypedStream *stream) +{ + char buffer[80]; + sprintf(buffer, "GNU TypedStream %d", OBJC_TYPED_STREAM_VERSION); + stream->version = OBJC_TYPED_STREAM_VERSION; + (*stream->write) (stream->physical, buffer, strlen (buffer) + 1); + return 1; +} + +static void __objc_finish_write_root_object(struct objc_typed_stream *stream) +{ + objc_hash_delete (stream->object_table); + stream->object_table = objc_hash_new (64, + (hash_func_type) objc_hash_ptr, + (compare_func_type) objc_compare_ptrs); +} + +static void __objc_finish_read_root_object(struct objc_typed_stream *stream) +{ + node_ptr node; + SEL awake_sel = sel_get_any_uid ("awake"); + cache_ptr free_list = objc_hash_new (64, + (hash_func_type) objc_hash_ptr, + (compare_func_type) objc_compare_ptrs); + + /* resolve object forward references */ + for (node = objc_hash_next (stream->object_refs, NULL); node; + node = objc_hash_next (stream->object_refs, node)) + { + struct objc_list *reflist = node->value; + const void *key = node->key; + id object = objc_hash_value_for_key (stream->object_table, key); + while (reflist) + { + *((id*) reflist->head) = object; + if (objc_hash_value_for_key (free_list,reflist) == NULL) + objc_hash_add (&free_list,reflist,reflist); + + reflist = reflist->tail; + } + } + + /* apply __objc_free to all objects stored in free_list */ + for (node = objc_hash_next (free_list, NULL); node; + node = objc_hash_next (free_list, node)) + objc_free ((void *) node->key); + + objc_hash_delete (free_list); + + /* empty object reference table */ + objc_hash_delete (stream->object_refs); + stream->object_refs = objc_hash_new (8, (hash_func_type) objc_hash_ptr, + (compare_func_type) objc_compare_ptrs); + + /* call -awake for all objects read */ + if (awake_sel) + { + for (node = objc_hash_next (stream->object_table, NULL); node; + node = objc_hash_next (stream->object_table, node)) + { + id object = node->value; + if (__objc_responds_to (object, awake_sel)) + (*objc_msg_lookup (object, awake_sel)) (object, awake_sel); + } + } + + /* empty object table */ + objc_hash_delete (stream->object_table); + stream->object_table = objc_hash_new(64, + (hash_func_type)objc_hash_ptr, + (compare_func_type)objc_compare_ptrs); +} + +/* +** Open the stream PHYSICAL in MODE +*/ + +TypedStream * +objc_open_typed_stream (FILE *physical, int mode) +{ + TypedStream *s = (TypedStream *) objc_malloc (sizeof (TypedStream)); + + s->mode = mode; + s->physical = physical; + s->stream_table = objc_hash_new (64, + (hash_func_type) objc_hash_ptr, + (compare_func_type) objc_compare_ptrs); + s->object_table = objc_hash_new (64, + (hash_func_type) objc_hash_ptr, + (compare_func_type) objc_compare_ptrs); + s->eof = (objc_typed_eof_func) __objc_feof; + s->flush = (objc_typed_flush_func) fflush; + s->writing_root_p = 0; + if (mode == OBJC_READONLY) + { + s->class_table + = objc_hash_new (8, (hash_func_type) objc_hash_string, + (compare_func_type) objc_compare_strings); + s->object_refs = objc_hash_new (8, (hash_func_type) objc_hash_ptr, + (compare_func_type) objc_compare_ptrs); + s->read = (objc_typed_read_func) __objc_fread; + s->write = (objc_typed_write_func) __objc_no_write; + __objc_read_typed_stream_signature (s); + } + else if (mode == OBJC_WRITEONLY) + { + s->class_table = 0; + s->object_refs = 0; + s->read = (objc_typed_read_func) __objc_no_read; + s->write = (objc_typed_write_func) __objc_fwrite; + __objc_write_typed_stream_signature (s); + } + else + { + objc_close_typed_stream (s); + return NULL; + } + s->type = OBJC_FILE_STREAM; + return s; +} + +/* +** Open the file named by FILE_NAME in MODE +*/ + +TypedStream* +objc_open_typed_stream_for_file (const char *file_name, int mode) +{ + FILE *file = NULL; + TypedStream *s; + + if (mode == OBJC_READONLY) + file = fopen (file_name, "r"); + else + file = fopen (file_name, "w"); + + if (file) + { + s = objc_open_typed_stream (file, mode); + if (s) + s->type |= OBJC_MANAGED_STREAM; + return s; + } + else + return NULL; +} + +/* +** Close STREAM freeing the structure it self. If it was opened with +** objc_open_typed_stream_for_file, the file will also be closed. +*/ + +void +objc_close_typed_stream (TypedStream *stream) +{ + if (stream->mode == OBJC_READONLY) + { + __objc_finish_read_root_object (stream); /* Just in case... */ + objc_hash_delete (stream->class_table); + objc_hash_delete (stream->object_refs); + } + + objc_hash_delete (stream->stream_table); + objc_hash_delete (stream->object_table); + + if (stream->type == (OBJC_MANAGED_STREAM | OBJC_FILE_STREAM)) + fclose ((FILE *)stream->physical); + + objc_free(stream); +} + +BOOL +objc_end_of_typed_stream (TypedStream *stream) +{ + return (*stream->eof) (stream->physical); +} + +void +objc_flush_typed_stream (TypedStream *stream) +{ + (*stream->flush) (stream->physical); +} + +long +objc_get_stream_class_version (TypedStream *stream, Class class) +{ + if (stream->class_table) + return PTR2LONG(objc_hash_value_for_key (stream->class_table, + class->name)); + else + return class_get_version (class); +} + |