Annotation of objc/typedstream.m, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
                      3:  *
                      4:  * @APPLE_LICENSE_HEADER_START@
                      5:  * 
                      6:  * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
                      7:  * Reserved.  This file contains Original Code and/or Modifications of
                      8:  * Original Code as defined in and that are subject to the Apple Public
                      9:  * Source License Version 1.0 (the 'License').  You may not use this file
                     10:  * except in compliance with the License.  Please obtain a copy of the
                     11:  * License at http://www.apple.com/publicsource and read it before using
                     12:  * this file.
                     13:  * 
                     14:  * The Original Code and all software distributed under the License are
                     15:  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
                     16:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
                     17:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
                     18:  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
                     19:  * License for the specific language governing rights and limitations
                     20:  * under the License."
                     21:  * 
                     22:  * @APPLE_LICENSE_HEADER_END@
                     23:  */
                     24: /*
                     25:     typedstream.m
                     26:     Pickling data structures
                     27:     Copyright 1989, NeXT, Inc.
                     28:     Responsability: Bertrand Serlet
                     29: */
                     30: 
                     31: #ifndef KERNEL
                     32: #ifdef SHLIB
                     33: #import "shlib.h"
                     34: #endif SHLIB
                     35: 
                     36: #import <stdarg.h>
                     37: #import <mach/mach.h>
                     38: #import <sys/file.h>
                     39: #import "objc-private.h"
                     40: #import "Object.h"
                     41: #import "objc-class.h"
                     42: #import "objc-runtime.h"
                     43: #import "typedstream.h"
                     44: #import "typedstreamprivate.h"
                     45: #import "hashtable.h"
                     46: 
                     47: #import <architecture/byte_order.h>
                     48: 
                     49: /*************************************************************************
                     50:  *     Utilities
                     51:  *************************************************************************/
                     52: 
                     53: static void checkRead (TypedStream *s) {
                     54:     if (s->write) 
                     55:        NX_RAISE (TYPEDSTREAM_CALLER_ERROR, "expecting a reading stream", s);
                     56:     };
                     57:     
                     58: static void checkWrite (TypedStream *s) {
                     59:     if (! s->write) 
                     60:        NX_RAISE (TYPEDSTREAM_CALLER_ERROR, "expecting a writing stream", s);
                     61:     };
                     62: 
                     63: static void checkExpected (const char *readType, const char *wanted) {
                     64:     if (readType == wanted) return;
                     65:     if (! readType || strcmp (readType, wanted)) {
                     66:        char    *buffer = malloc (
                     67:                ((readType) ? strlen (readType) : 0) + strlen (wanted) + 100);
                     68:        sprintf (buffer, "file inconsistency: read '%s', expecting '%s'", 
                     69:            readType, wanted);
                     70:        NX_RAISE (TYPEDSTREAM_FILE_INCONSISTENCY, buffer, 0);
                     71:        };
                     72:     };
                     73:     
                     74: static volatile void classError (const char *className, const char *message) {
                     75:     char       *buffer = malloc (100);
                     76:     sprintf (buffer, "class error for '%s': %s", className, message);
                     77:     NX_RAISE (TYPEDSTREAM_CLASS_ERROR, buffer, 0);
                     78:     };
                     79:     
                     80: static volatile void typeDescriptorError (char ch, const char *message) {
                     81:     char       *buffer = malloc (100);
                     82:     sprintf (buffer, "type descriptor error for '%c': %s", ch, message);
                     83:     NX_RAISE (TYPEDSTREAM_TYPE_DESCRIPTOR_ERROR, buffer, 0);
                     84:     };
                     85:     
                     86: static volatile void writeRefError (const char *message) {
                     87:     NX_RAISE (TYPEDSTREAM_WRITE_REFERENCE_ERROR, message, 0);
                     88:     };
                     89:     
                     90: static BOOL sameSexAsCube () {
                     91:     union {int ii; char cc[sizeof(int)];}      uu;
                     92:     
                     93:     uu.ii = 1;
                     94:     if (uu.cc[sizeof(int)-1] == 1) return TRUE;
                     95:     if (uu.cc[0] == 1) return FALSE;
                     96:     NX_RAISE (TYPEDSTREAM_FILE_INCONSISTENCY, 
                     97:        "typedstream: snail? cannot recognize byte sex.", 
                     98:        (void *) (int) uu.cc[0]);
                     99:     };
                    100:     
                    101: /*************************************************************************
                    102:  *     Low-Level: encoding tightly information, 
                    103:  *     and sharing pointers and strings
                    104:  *************************************************************************/
                    105: 
                    106: #define LONG2LABEL     -127
                    107: #define LONG4LABEL     -126
                    108: #define REALLABEL      -125
                    109: #define NEWLABEL       -124
                    110: #define NULLLABEL      -123
                    111: #define EOOLABEL       -122
                    112: /* more reserved labels */
                    113: #define SMALLESTLABEL  -110
                    114: #define Bias(x) (x - SMALLESTLABEL)
                    115: 
                    116: /* Following constants are not universal constants, but used for the coding */
                    117: #define BIG_INT +2147483647L
                    118: #define SMALL_INT -2147483647L
                    119: #define BIG_SHORT +32767
                    120: #define SMALL_SHORT -32767
                    121: 
                    122: #define INIT_NSTRINGS 252
                    123: #define INIT_NPTRS 252
                    124: #define PTRS(x) ((void **)x)
                    125: #define STRINGS(x) ((char **)x)
                    126: 
                    127: static inline int inc_stringCounter(_CodingStream *coder) {
                    128:   if (coder->stringCounter == coder->stringCounterMax) {
                    129:     coder->stringCounterMax += INIT_NSTRINGS;
                    130:     coder->strings = (NXHashTable *)
                    131:        NXZoneRealloc(coder->scratch, coder->strings, coder->stringCounterMax * sizeof(char *));
                    132:   }
                    133:   return coder->stringCounter++;
                    134: }
                    135: 
                    136: static int inc_ptrCounter(_CodingStream *coder) {
                    137:   if (coder->ptrCounter == coder->ptrCounterMax) {
                    138:     coder->ptrCounterMax += INIT_NPTRS;
                    139:     coder->ptrs = (NXHashTable *)
                    140:        NXZoneRealloc(coder->scratch, coder->ptrs, coder->ptrCounterMax * sizeof(void *));
                    141:   }
                    142:   return coder->ptrCounter++;
                    143: }
                    144: 
                    145: typedef struct {
                    146:     const char *key;
                    147:     int                value;
                    148:     } StrToInt;
                    149: 
                    150: static void freeStrToInt(const void *info, void *data) {
                    151:     free((char *)((StrToInt *)data)->key);
                    152:     free(data);
                    153: }
                    154: 
                    155: typedef struct {
                    156:     const void *key;
                    157:     int                value;
                    158:     } PtrToInt;
                    159: 
                    160: typedef struct {
                    161:     int                key;
                    162:     void       *value;
                    163:     } IntToPtr;
                    164: 
                    165:    
                    166: /* Creation, destruction of the low-level streams */
                    167: static _CodingStream *_NXOpenEncodingStream (NXStream *physical) {
                    168:     _CodingStream              *coder;
                    169:     NXHashTablePrototype       proto = NXStrStructKeyPrototype;
                    170:     
                    171:     coder = (_CodingStream *) malloc (sizeof (_CodingStream));
                    172:     coder->physical = physical;
                    173:     coder->swap = NO;
                    174:     coder->write = YES;
                    175:     proto.free = freeStrToInt;
                    176:     coder->strings = NXCreateHashTable (proto, 7, NULL);
                    177:     coder->stringCounter = SMALLESTLABEL;
                    178:     coder->ptrs = NXCreateHashTable (NXPtrStructKeyPrototype, 15, NULL);
                    179:     coder->ptrCounter = SMALLESTLABEL;
                    180:     return coder;
                    181:     };
                    182: 
                    183: static _CodingStream *_NXOpenDecodingStream (NXStream *physical) {
                    184:     _CodingStream              *coder;
                    185:     NXZone *zone;
                    186:     
                    187:     zone = NXCreateZone(vm_page_size, vm_page_size, 1);
                    188:     coder = (_CodingStream *) NXZoneMalloc (zone, sizeof (_CodingStream));
                    189:     coder->scratch = zone;
                    190:     coder->physical = physical;
                    191:     coder->swap = NO;
                    192:     coder->write = NO;
                    193:     coder->strings = (NXHashTable *)
                    194:        NXZoneMalloc(coder->scratch, INIT_NSTRINGS * sizeof(char *));
                    195:     coder->stringCounter = 0;
                    196:     coder->stringCounterMax = INIT_NSTRINGS;
                    197:     coder->ptrs = (NXHashTable *)
                    198:        NXZoneMalloc(coder->scratch, INIT_NPTRS * sizeof(void *));
                    199:     coder->ptrCounter = 0;
                    200:     coder->ptrCounterMax = INIT_NPTRS;
                    201:     return coder;
                    202:     };
                    203: 
                    204: static BOOL _NXEndOfCodingStream (_CodingStream *coder) {
                    205:     char       ch = NXGetc (coder->physical);
                    206:     NXUngetc (coder->physical);
                    207:     return ch == -1;
                    208:     };
                    209: 
                    210: static void _NXCloseCodingStream (_CodingStream *coder) {
                    211:   if (coder->write) {
                    212:        NXFreeHashTable(coder->strings);
                    213:        NXFreeHashTable(coder->ptrs);
                    214:        free(coder);
                    215:     } else 
                    216:        NXDestroyZone(coder->scratch);
                    217:   };
                    218: 
                    219: /* Byte Swapping utilities */
                    220: 
                    221: static __inline__ unsigned long long
                    222: _NXSwapCard8(_CodingStream *coder, unsigned long long llin) {
                    223:     if (coder->swap)
                    224:        return NXSwapLongLong(llin);
                    225:     else
                    226:        return llin;
                    227:     };
                    228: 
                    229: static __inline__ unsigned int
                    230: _NXSwapCard4(_CodingStream *coder, unsigned int iin) {
                    231:     if (coder->swap)
                    232:        return NXSwapInt(iin);
                    233:     else
                    234:        return iin;
                    235:     };
                    236: 
                    237: static __inline__ unsigned short
                    238: _NXSwapCard2(_CodingStream *coder, unsigned short sin) {
                    239:     if (coder->swap)
                    240:        return NXSwapShort(sin);
                    241:     else
                    242:        return sin;
                    243:     };
                    244: 
                    245: static __inline__ float
                    246: _NXSwapFloat(_CodingStream *coder, NXSwappedFloat fin) {
                    247:     if (coder->swap) {
                    248:        return NXConvertSwappedFloatToHost(NXSwapFloat(fin));
                    249:     } else
                    250:        return NXConvertSwappedFloatToHost(fin);
                    251:     };
                    252: 
                    253: static __inline__ double
                    254: _NXSwapDouble(_CodingStream *coder, NXSwappedDouble din) {
                    255:     if (coder->swap) {
                    256:        return NXConvertSwappedDoubleToHost(NXSwapDouble(din));
                    257:     } else
                    258:        return NXConvertSwappedDoubleToHost(din);
                    259:     };
                    260: 
                    261: 
                    262: /* Encoding/Decoding of usual quantities */
                    263: 
                    264: static void _NXEncodeBytes (_CodingStream *coder, const char *buf, int count) {
                    265:     if (coder->physical) NXWrite (coder->physical, buf, count);
                    266:     };
                    267:     
                    268: static void _NXDecodeBytes (_CodingStream *coder, char *buf, int count) {
                    269:     NXRead (coder->physical, buf, count);
                    270:     };
                    271: 
                    272: static void _NXEncodeChar (_CodingStream *coder, signed char c) {
                    273:     if (coder->physical) NXPutc (coder->physical, c);
                    274:     };
                    275:     
                    276: static signed char _NXDecodeChar (_CodingStream *coder) {
                    277:     return NXGetc (coder->physical);
                    278:     };
                    279: 
                    280: /* all the following should (of course) be made machine independent */
                    281: static void _NXEncodeShort (_CodingStream *coder, short x) {
                    282:     if (x>=SMALLESTLABEL && x<=127)
                    283:        _NXEncodeChar (coder, (signed char) x);
                    284:     else {
                    285:        _NXEncodeChar (coder, LONG2LABEL); 
                    286:        _NXEncodeBytes (coder, (char *) &x, 2);
                    287:        };
                    288:     };
                    289:     
                    290: static short _NXDecodeShort (_CodingStream *coder) {
                    291:     signed char        ch = _NXDecodeChar (coder);
                    292:     short      x;
                    293:     if (ch != LONG2LABEL) return (short) ch;
                    294:     _NXDecodeBytes (coder, (char *) &x, 2);
                    295:     x = _NXSwapCard2(coder, x);
                    296:     return x;
                    297:     };
                    298:        
                    299: static void _NXEncodeInt (_CodingStream *coder, int x) {
                    300:     if (x >= SMALLESTLABEL && x <= 127)
                    301:        _NXEncodeChar (coder, (signed char) x);
                    302:     else if (x >= SMALL_SHORT && x <= BIG_SHORT) {
                    303:        short   sh = (short) x;
                    304:        _NXEncodeChar (coder, LONG2LABEL);
                    305:        _NXEncodeBytes (coder, (char *) &sh, 2);
                    306:        }
                    307:     else {
                    308:        _NXEncodeChar (coder, LONG4LABEL);
                    309:        _NXEncodeBytes (coder, (char *) &x, 4);
                    310:        };
                    311:     };
                    312: 
                    313: /* Finishes to decode an int. ch can be only be LONG2LABEL, LONG4LABEL or the int itself. */
                    314: static int FinishDecodeInt (_CodingStream *coder, signed char ch) {
                    315:     switch (ch) {
                    316:        case LONG2LABEL: {
                    317:            short       x;
                    318:            _NXDecodeBytes (coder, (char *) &x, 2);
                    319:            x = _NXSwapCard2(coder, x);
                    320:            return (int) x;
                    321:            };
                    322:        case LONG4LABEL: {
                    323:            int         x;
                    324:            _NXDecodeBytes (coder, (char *) &x, 4);
                    325:            x = _NXSwapCard4(coder, x);
                    326:            return x;
                    327:            };
                    328:        default: return (int) ch;
                    329:        };
                    330:     };
                    331:     
                    332: static int _NXDecodeInt (_CodingStream *coder) {
                    333:     return FinishDecodeInt (coder, _NXDecodeChar (coder));
                    334:     };
                    335:        
                    336: static int FloorOrZero (double x) {
                    337:     if (x >= (double) BIG_INT-1) return 0;
                    338:     if (x < (double) SMALL_INT+1) return 0;
                    339:     return (int) (float) x;
                    340:     };
                    341:     
                    342: static void _NXEncodeFloat (_CodingStream *coder, float x) {
                    343:     int                flore = FloorOrZero ((double) x);
                    344:     /* we are conservative */
                    345:     if ((float) flore == x) _NXEncodeInt (coder, flore); 
                    346:     else {
                    347:         NXSwappedFloat sf = NXConvertHostFloatToSwapped(x);
                    348:        _NXEncodeChar (coder, REALLABEL);
                    349:         _NXEncodeBytes (coder, (char *) &sf, sizeof(NXSwappedFloat));
                    350:        };
                    351:     };
                    352: 
                    353: static float _NXDecodeFloat (_CodingStream *coder) {
                    354:     signed char        ch = _NXDecodeChar (coder);
                    355:     NXSwappedFloat x;
                    356:     if (ch != REALLABEL) return (float) FinishDecodeInt (coder, ch);
                    357:     _NXDecodeBytes (coder, (char *) &x, sizeof(NXSwappedFloat));
                    358:     return _NXSwapFloat(coder, x);
                    359:     };
                    360:        
                    361: static void _NXEncodeDouble (_CodingStream *coder, double x) {
                    362:     int                flore = FloorOrZero (x);
                    363:     if ((double) flore == x) _NXEncodeInt (coder, flore);
                    364:     else {
                    365:         NXSwappedDouble sd = NXConvertHostDoubleToSwapped(x);
                    366:        _NXEncodeChar (coder, REALLABEL);
                    367:         _NXEncodeBytes (coder, (char *) &sd, sizeof(NXSwappedDouble));
                    368:        };
                    369:     };
                    370: 
                    371: static double _NXDecodeDouble (_CodingStream *coder) {
                    372:     signed char        ch = _NXDecodeChar (coder);
                    373:     NXSwappedDouble    x;
                    374:     if (ch != REALLABEL) return (double) FinishDecodeInt (coder, ch);
                    375:     _NXDecodeBytes (coder, (char *) &x, sizeof(NXSwappedDouble));
                    376:     return _NXSwapDouble(coder, x);
                    377:     };
                    378:        
                    379: static void _NXEncodeChars (_CodingStream *coder, const char *str) {
                    380:     int                len;
                    381:     if (! str) {_NXEncodeChar (coder, NULLLABEL); return;};
                    382:     len = strlen (str);
                    383:     _NXEncodeInt (coder, len);
                    384:     _NXEncodeBytes (coder, str, len);
                    385:     };
                    386: 
                    387: static char *_NXDecodeChars (_CodingStream *coder, NXZone *zone) {
                    388:     signed char        ch = _NXDecodeChar (coder);
                    389:     int                len;
                    390:     STR                str;
                    391:     if (ch == NULLLABEL) return NULL;
                    392:     len = FinishDecodeInt (coder, ch);
                    393:     str = (STR) NXZoneMalloc (zone, len+1);
                    394:     _NXDecodeBytes (coder, str, len);
                    395:     str[len] = '\0';
                    396:     return str;
                    397:     };
                    398:        
                    399: /* Encoding/Decoding of shared quantities.  The trick is that a label int cannot start by encoding NEWLABEL */
                    400: static void _NXEncodeSharedString (_CodingStream *coder, const char *str) {
                    401:     StrToInt   sti;
                    402:     
                    403:     if (! str) {_NXEncodeChar (coder, NULLLABEL); return;};
                    404:     sti.key = str;
                    405:     if (NXHashMember (coder->strings, &sti)) {
                    406:        int     value = ((StrToInt *) NXHashGet (coder->strings, &sti))->value;
                    407:        _NXEncodeInt (coder, value);
                    408:        return;
                    409:        }
                    410:      else {
                    411:         StrToInt       *new = (StrToInt *) malloc (sizeof (StrToInt));
                    412:        _NXEncodeChar (coder, NEWLABEL); _NXEncodeChars (coder, str);
                    413:        new->key = NXCopyStringBuffer(str); 
                    414:        new->value = coder->stringCounter++;
                    415:        (void) NXHashInsert (coder->strings, new);
                    416:        return;
                    417:        };
                    418:     };
                    419:     
                    420: static char *_NXDecodeSharedString (_CodingStream *coder) {
                    421:     signed char        ch = _NXDecodeChar (coder);
                    422:     
                    423:     if (ch == NULLLABEL) return NULL;
                    424:     if (ch == NEWLABEL) {
                    425:        char *s = _NXDecodeChars (coder, coder->scratch);
                    426:        STRINGS(coder->strings)[inc_stringCounter(coder)] = s;
                    427:        return s;
                    428:        };
                    429:     return STRINGS(coder->strings)[Bias(FinishDecodeInt(coder, ch))];
                    430:     };
                    431: 
                    432: static const char *_NXDecodeUniqueString (_CodingStream *coder) {
                    433:     return NXUniqueString (_NXDecodeSharedString (coder));
                    434:     };
                    435:     
                    436: static BOOL _NXEncodeShared (_CodingStream *coder, const void *ptr) {
                    437:     PtrToInt   pti;
                    438:     
                    439:     if (! ptr) {
                    440:        _NXEncodeChar (coder, NULLLABEL);
                    441:        return NO;
                    442:        };
                    443:     pti.key = ptr;
                    444:     if (NXHashMember (coder->ptrs, &pti)) {
                    445:        int     value = ((PtrToInt *) NXHashGet (coder->ptrs, &pti))->value;
                    446:        _NXEncodeInt (coder, value);
                    447:        return NO;
                    448:        }
                    449:      else {
                    450:         PtrToInt       *new = (PtrToInt *) malloc (sizeof (PtrToInt));
                    451:        _NXEncodeChar (coder, NEWLABEL);
                    452:        new->key = ptr; new->value = coder->ptrCounter++;
                    453:        (void) NXHashInsert (coder->ptrs, new);
                    454:        return YES;
                    455:        };
                    456:     };
                    457:     
                    458: /*
                    459: static BOOL _NXDecodeShared (_CodingStream *coder, void **pptr, int *label) {
                    460:     signed char        ch = _NXDecodeChar (coder);
                    461:     
                    462:     if (ch == NULLLABEL) {
                    463:        *pptr = NULL;
                    464:        return NO;
                    465:        };
                    466:     if (ch == NEWLABEL) {
                    467:        *label = coder->ptrCounter++;
                    468:        return YES;
                    469:        }
                    470:     else {
                    471:        IntToPtr        itp;
                    472:        
                    473:        *label = FinishDecodeInt (coder, ch);
                    474:        itp.key = *label;
                    475:        *pptr = ((IntToPtr *) NXHashGet (coder->ptrs, &itp))->value;
                    476:        return NO;
                    477:        };
                    478:     };
                    479: */
                    480:     
                    481: //?? we have a minor leak here (8 bytes per unarchived Font or Bitmap) that could be fixed by replacing for the entry label the previous value with object, instead of allocating a new entry.  If that was done, we could check in _NXNoteShared that there was no previous value.
                    482: /*
                    483: static void _NXNoteShared (_CodingStream *coder, void *ptr, int label) {
                    484:     IntToPtr   *new = (IntToPtr *) malloc (sizeof (IntToPtr));
                    485:     
                    486:     if (! ptr) 
                    487:        NX_RAISE (TYPEDSTREAM_INTERNAL_ERROR, 
                    488:                "_NXNoteShared: nil shared", (void *) label);
                    489:     new->key = label; new->value = ptr;
                    490:     new = NXHashInsert (coder->ptrs, new);
                    491:     if (new) free (new);
                    492:     };
                    493: */
                    494:     
                    495: static void _NXEncodeString (_CodingStream *coder, const char *str) {
                    496:     if (_NXEncodeShared (coder, str)) _NXEncodeSharedString (coder, str);
                    497:     };
                    498: 
                    499: /* except in the case of nil, always returns a freshly allocated string */
                    500: static char *_NXDecodeString (_CodingStream *coder, NXZone *zone) {
                    501:   char *str;
                    502:   char ch;
                    503:   switch (ch = NXGetc(coder->physical)) {
                    504:   case NULLLABEL: return NULL;
                    505:   case NEWLABEL: {
                    506:     str = _NXDecodeSharedString (coder);
                    507:     PTRS(coder->ptrs)[inc_ptrCounter(coder)] = str;
                    508:     /* we make a real copy (let's not take risks!) */
                    509:     return NXCopyStringBufferFromZone (str, zone);
                    510:   }
                    511:   default: return NXCopyStringBufferFromZone (PTRS(coder->ptrs)[Bias(FinishDecodeInt(coder, ch))], zone);
                    512:   }
                    513: }
                    514: 
                    515: /***********************************************************************
                    516:  *     Creation, destruction and other global operations
                    517:  **********************************************************************/
                    518: 
                    519: #define FIRSTSTREAMERVERSION   3
                    520: #define NXSTREAMERVERSION      4
                    521: 
                    522: /* The two following strings are magic: they decide of the byte oredering encoding.  It is very important that they remain small, so that only bytes are written for writting the string themselves */
                    523: #define NXSTREAMERNAME         "typedstream"
                    524: #define NXNAMESTREAMER         "streamtyped"
                    525: 
                    526: /* A typed stream always encodes the type of the information before its value.  Objects are specially coded because first the class is coded (along with the nbytesIndexedVars, then all its data (by sending a write: or read: message is sent to the object, and finally an end of Object token (NULLLABEL). */
                    527: 
                    528: NXTypedStream *NXOpenTypedStream (NXStream *physical, int mode) {
                    529:     TypedStream                *s;
                    530:     
                    531:     if (mode != NX_WRITEONLY && mode != NX_READONLY)
                    532:        NX_RAISE (TYPEDSTREAM_CALLER_ERROR, 
                    533:                "NXOpenTypedStream: invalid mode", (void *) mode);
                    534:     if (! physical)
                    535:        NX_RAISE (TYPEDSTREAM_CALLER_ERROR, 
                    536:                "NXOpenTypedStream: null stream", 0);
                    537:     s = (TypedStream *) malloc (sizeof (TypedStream));
                    538:     s->coder = (mode == NX_WRITEONLY) 
                    539:        ? _NXOpenEncodingStream (physical) : _NXOpenDecodingStream (physical);
                    540:     s->ids = NULL;
                    541:     s->write = mode == NX_WRITEONLY;
                    542:     s->noteConditionals = NO;
                    543:     s->doStatistics = NO;
                    544:     s->fileName = NULL;
                    545:     s->classVersions = NULL;
                    546:     s->objectZone = NXDefaultMallocZone();
                    547:     if (s->write) {
                    548:        _NXEncodeChar (s->coder, NXSTREAMERVERSION);
                    549:        _NXEncodeChars (s->coder, (sameSexAsCube()) ? NXSTREAMERNAME : NXNAMESTREAMER);
                    550:        _NXEncodeInt (s->coder, NXSYSTEMVERSION);
                    551:        s->streamerVersion = NXSTREAMERVERSION;
                    552:        s->systemVersion = NXSYSTEMVERSION;
                    553:        }
                    554:     else {
                    555:        STR     header;
                    556:        
                    557:        s->streamerVersion = _NXDecodeChar (s->coder);
                    558:        if (s->streamerVersion < FIRSTSTREAMERVERSION ||
                    559:                s->streamerVersion > NXSTREAMERVERSION) {
                    560:            /* reading an old version or reading a non-archive file */
                    561:            NXUngetc (s->coder->physical);
                    562:            NXCloseTypedStream (s);
                    563:            return NULL;
                    564:            };
                    565:        header = _NXDecodeChars (s->coder, s->coder->scratch);
                    566:        if (strcmp (header, NXSTREAMERNAME) == 0)
                    567:            s->coder->swap = ! sameSexAsCube();
                    568:        else if (strcmp (header, NXNAMESTREAMER) == 0)
                    569:            s->coder->swap = sameSexAsCube();
                    570:        else {
                    571:            /* we are not reading a typedstream file! */
                    572:            NXCloseTypedStream (s);
                    573:            return NULL;
                    574:            };
                    575:        /* we are now ready for int reading */
                    576:        s->systemVersion = _NXDecodeInt (s->coder);
                    577:        s->classVersions = NXCreateHashTableFromZone (
                    578:            NXStrStructKeyPrototype, 0, NULL, s->coder->scratch);
                    579:        free (header);
                    580:        };
                    581:     return s;
                    582:     };
                    583: 
                    584: extern void NXSetTypedStreamZone(NXTypedStream *stream, NXZone *zone)
                    585: {
                    586:     TypedStream        *s = (TypedStream *) stream;
                    587:     s->objectZone = zone;
                    588: }
                    589: 
                    590: extern NXZone *NXGetTypedStreamZone(NXTypedStream *stream)
                    591: {
                    592:     TypedStream        *s = (TypedStream *) stream;
                    593:     return s->objectZone;
                    594: }
                    595: 
                    596: BOOL NXEndOfTypedStream (NXTypedStream *stream) {
                    597:     TypedStream        *s = (TypedStream *) stream;
                    598:     checkRead (s);
                    599:     return _NXEndOfCodingStream (s->coder);
                    600:     };
                    601:     
                    602: void NXFlushTypedStream (NXTypedStream *stream) {
                    603:     TypedStream        *s = (TypedStream *) stream;
                    604:     
                    605:     checkWrite (s);
                    606:     if (s->coder->physical) NXFlush (s->coder->physical);
                    607:     };
                    608: 
                    609: void NXCloseTypedStream (NXTypedStream *stream) {
                    610:     int saveToFile = 0;
                    611:     TypedStream        *s = (TypedStream *) stream;
                    612:     if (s->write) NXFlushTypedStream (stream);
                    613:     if (s->classVersions) NXFreeHashTable (s->classVersions);
                    614:     if (s->fileName) {
                    615:        NXStream        *physical;
                    616:        physical = s->coder->physical;
                    617:        s->coder->physical = NULL;
                    618:        if (s->write) saveToFile = NXSaveToFile (physical, s->fileName);
                    619:        NXCloseMemory (physical, NX_FREEBUFFER);
                    620:        };
                    621:     _NXCloseCodingStream (s->coder);
                    622:     if (s->ids) NXFreeHashTable (s->ids);
                    623:     free (s);
                    624:     if (saveToFile) NX_RAISE (TYPEDSTREAM_CALLER_ERROR, "file cannot be saved", NULL);
                    625:     };
                    626: 
                    627: /***********************************************************************
                    628:  *     Writing and reading arbitrary data: type string utilities
                    629:  **********************************************************************/
                    630: 
                    631: // Handle both new- and old-style aggregate type descriptors.
                    632: // One of "{iiii}", "{foo=iiii}", or "{?=iiii}".
                    633: 
                    634: static const char *skipAggregateName (const char *string)
                    635: {
                    636:   const char *type = string;
                    637:   
                    638:   while (1)
                    639:     switch (*type++)
                    640:       {
                    641:       case '=':                // Succeeded: end of aggregate name
                    642:        return type;
                    643:        
                    644:       case '}':                // Failed: end of struct descriptor
                    645:       case '{':                // Failed: start of nested struct descriptor
                    646:       case ')':                // Failed: end of union descriptor
                    647:       case '(':                // Failed: start of nested union descriptor
                    648:       case '\0':       // Failed: end of descriptor!
                    649:        return string;
                    650:       }
                    651: }
                    652: 
                    653: static inline unsigned int roundUp (unsigned int size, unsigned int align)
                    654: {
                    655:   return align * ((size + align - 1) / align);
                    656: }
                    657: 
                    658: static inline unsigned int max (unsigned int x, unsigned int y)
                    659: {
                    660:   if (x >= y)
                    661:     return x;
                    662:   else
                    663:     return y;
                    664: }
                    665: 
                    666: // Some machines enforce a minimum structure alignment.  This should
                    667: // parallel the STRUCTURE_SIZE_BOUNDARY macro in the GNU compiler.
                    668: 
                    669: #if defined (m68k) && defined (NeXT)
                    670: #define MIN_STRUCT_ALIGN 2
                    671: #else
                    672: #define MIN_STRUCT_ALIGN 1
                    673: #endif
                    674: 
                    675: // Store the size and alignment of "type" into "sizePtr" and "alignPtr".
                    676: 
                    677: static const char *
                    678: SizeOfType (const char *type, unsigned int *sizePtr, unsigned int *alignPtr)
                    679: {
                    680:   char c = *type++;
                    681:   
                    682:   switch (c)
                    683:     {
                    684:     case 'c':
                    685:     case 'C':
                    686:       *sizePtr = sizeof (char);
                    687:       *alignPtr = __alignof (char);
                    688:       break;
                    689:       
                    690:     case 's':
                    691:     case 'S':
                    692:       *sizePtr = sizeof (short);
                    693:       *alignPtr = __alignof (short);
                    694:       break;
                    695:       
                    696:     case 'i':
                    697:     case 'I':
                    698:     case '!':
                    699:       *sizePtr = sizeof (int);
                    700:       *alignPtr = __alignof (int);
                    701:       break;
                    702:       
                    703:     case 'l':
                    704:     case 'L':
                    705:       *sizePtr = sizeof (long);
                    706:       *alignPtr = __alignof (long);
                    707:       break;
                    708:       
                    709:     case 'f':
                    710:       *sizePtr = sizeof (float);
                    711:       *alignPtr = __alignof (float);
                    712:       break;
                    713:       
                    714:     case 'd':
                    715:       *sizePtr = sizeof (double);
                    716:       *alignPtr = __alignof (double);
                    717:       break;
                    718:       
                    719:     case '@':
                    720:       *sizePtr = sizeof (id);
                    721:       *alignPtr = __alignof (id);
                    722:       break;
                    723:       
                    724:     case '*':
                    725:     case '%':
                    726:       *sizePtr = sizeof (char *);
                    727:       *alignPtr = __alignof (char *);
                    728:       break;
                    729:       
                    730:     case ':':
                    731:       *sizePtr = sizeof (SEL);
                    732:       *alignPtr = __alignof (SEL);
                    733:       break;
                    734:       
                    735:     case '#':
                    736:       *sizePtr = sizeof (Class);
                    737:       *alignPtr = __alignof (Class);
                    738:       break;
                    739:       
                    740:     case '[':
                    741:       {
                    742:        unsigned int count = 0;
                    743:        unsigned int size, align;
                    744:        
                    745:        while ('0' <= *type && *type <= '9')
                    746:          count = 10 * count + (*type++ - '0');
                    747:        
                    748:        type = SizeOfType (type, &size, &align);
                    749:        
                    750:        *sizePtr = count * roundUp (size, align);
                    751:        *alignPtr = align;
                    752:        
                    753:        c = *type++;
                    754:        if (c != ']')
                    755:          typeDescriptorError (c, "missing ']' in type descriptor");
                    756:        
                    757:        break;
                    758:       }
                    759:       
                    760:     case '{':
                    761:       {
                    762:         unsigned int structSize = 0;
                    763:        unsigned int structAlign = MIN_STRUCT_ALIGN;
                    764:        
                    765:        type = skipAggregateName (type);
                    766:        
                    767:        while (*type != '}')
                    768:          {
                    769:            unsigned int size, align;
                    770:            
                    771:            type = SizeOfType (type, &size, &align);
                    772:            
                    773:            structSize = roundUp (structSize, align);
                    774:            structSize += size;
                    775:            structAlign = max (structAlign, align);
                    776:          }
                    777:        
                    778:        *sizePtr = roundUp (structSize, structAlign);
                    779:        *alignPtr = structAlign;
                    780:        
                    781:        type++;
                    782:        break;
                    783:       }
                    784:       
                    785:     case '(':
                    786:       {
                    787:         unsigned int unionSize = 0;
                    788:        unsigned int unionAlign = 1;
                    789:        
                    790:        type = skipAggregateName (type);
                    791:        
                    792:        while (*type != ')')
                    793:          {
                    794:            unsigned int size, align;
                    795:            
                    796:            type = SizeOfType (type, &size, &align);
                    797:            
                    798:            unionSize = max (unionSize, size);
                    799:            unionAlign = max (unionAlign, align);
                    800:          }
                    801:        
                    802:        *sizePtr = roundUp (unionSize, unionAlign);
                    803:        *alignPtr = unionAlign;
                    804:        
                    805:        type++;
                    806:        break;
                    807:       }
                    808:       
                    809:     default:
                    810:       typeDescriptorError (c, "unknown type descriptor");
                    811:     }
                    812:   
                    813:   return type;
                    814: }
                    815: 
                    816: 
                    817: /***********************************************************************
                    818:  *     Writing and reading arbitrary data: the real Write/Read of values
                    819:  **********************************************************************/
                    820: 
                    821: // Write one object described by "type" and store it into "arg".
                    822: // Returns the unread portion of "type".
                    823: 
                    824: static const char *
                    825: WriteValue (NXTypedStream *stream, const char *type, const void *arg)
                    826: {
                    827:   TypedStream *s = (TypedStream *) stream;
                    828:   char c = *type++;
                    829:   
                    830:   switch (c)
                    831:     {
                    832:     case 'c':
                    833:     case 'C':
                    834:       _NXEncodeChar (s->coder, *((signed char *) arg));
                    835:       break;
                    836:       
                    837:     case 's':
                    838:     case 'S': 
                    839:       _NXEncodeShort (s->coder, *((short *) arg));
                    840:       break;
                    841:       
                    842:     case 'i':
                    843:     case 'I':
                    844:     case 'l':
                    845:     case 'L':
                    846:       _NXEncodeInt (s->coder, *((int *) arg));
                    847:       break;
                    848:       
                    849:     case 'f': 
                    850:       _NXEncodeFloat (s->coder, *((float *) arg));
                    851:       break;
                    852:       
                    853:     case 'd':
                    854:       _NXEncodeDouble (s->coder, *((double *) arg));
                    855:       break;
                    856:       
                    857:     case '@':
                    858:       InternalWriteObject (s, *((id *) arg));
                    859:       break;
                    860:       
                    861:     case '*':
                    862:       _NXEncodeString (s->coder, *((const char **) arg));
                    863:       break;
                    864:       
                    865:     case '%':
                    866:       _NXEncodeSharedString (s->coder, *((const char **) arg));
                    867:       break;
                    868:       
                    869:     case ':':
                    870:       _NXEncodeSharedString (s->coder, sel_getName (*((SEL *) arg)));
                    871:       break;
                    872:       
                    873:     case '#': 
                    874:       NXWriteClass (stream, *((Class *) arg));
                    875:       break;
                    876:       
                    877:     case '[':
                    878:       {
                    879:        unsigned int size, align;
                    880:        unsigned int i, count = 0;
                    881:        const char *elementType;
                    882:        
                    883:        while ('0' <= *type && *type <= '9')
                    884:          count = 10 * count + (*type++ - '0');
                    885:        
                    886:        elementType = type;
                    887:        type = SizeOfType (elementType, &size, &align);
                    888:        
                    889:        for (i = 0; i < count; i++)
                    890:          WriteValue (stream, elementType, ((char *) arg) + (i * size));
                    891:        
                    892:        c = *type++;
                    893:        if (c != ']')
                    894:          typeDescriptorError (c, "missing ']' in type descriptor");
                    895:        
                    896:        break;
                    897:       }
                    898:       
                    899:     case '{':
                    900:       {
                    901:         unsigned int offset = 0;
                    902:        
                    903:        type = skipAggregateName (type);
                    904:        
                    905:        while (*type != '}')
                    906:          {
                    907:            unsigned int size, align;
                    908:            
                    909:            SizeOfType (type, &size, &align);
                    910:            
                    911:            offset = roundUp (offset, align);
                    912:            
                    913:            type = WriteValue (stream, type, ((char *) arg) + offset);
                    914:            
                    915:            offset += size;
                    916:          }
                    917:        
                    918:        type++;
                    919:        break;
                    920:       }
                    921:       
                    922:     case '(':
                    923:       {
                    924:        unsigned int i, size, align;
                    925:        const char *newType;
                    926:        
                    927:        type = skipAggregateName (type);
                    928:        
                    929:        type = SizeOfType (type - 1, &size, &align);
                    930:        
                    931:        for (i = 0; i < size; i++)
                    932:          newType = WriteValue (stream, "C", ((char *) arg) + i);
                    933:        
                    934:        break;
                    935:       }
                    936:       
                    937:     case '!': 
                    938:       break;
                    939:       
                    940:     default:
                    941:       typeDescriptorError (c, "unknown type descriptor");
                    942:     }
                    943:   
                    944:   return type;
                    945: }
                    946: 
                    947: 
                    948: // Read one object described by "type" ponted to by "arg".
                    949: // Returns the unread portion of "type".
                    950: 
                    951: static const char *
                    952: ReadValue (NXTypedStream *stream, const char *type, void *arg)
                    953: {
                    954:   TypedStream *s = (TypedStream *) stream;
                    955:   char c = *type++;
                    956:   
                    957:   switch (c)
                    958:     {
                    959:     case 'c':
                    960:     case 'C':
                    961:       *((char *) arg) = _NXDecodeChar (s->coder);
                    962:       break;
                    963:       
                    964:     case 's':
                    965:     case 'S':
                    966:       *((short *) arg) = _NXDecodeShort (s->coder);
                    967:       break;
                    968:       
                    969:     case 'i':
                    970:     case 'I':
                    971:     case 'l':
                    972:     case 'L':
                    973:       *((int *) arg) = _NXDecodeInt (s->coder);
                    974:       break;
                    975:       
                    976:     case 'f':
                    977:       *((float *) arg) = _NXDecodeFloat (s->coder);
                    978:       break;
                    979:       
                    980:     case 'd':
                    981:       *((double *) arg) = _NXDecodeDouble (s->coder);
                    982:       break;
                    983:       
                    984:     case '@':
                    985:       *((id *) arg) = InternalReadObject (s);
                    986:       break;
                    987:       
                    988:     case '*':
                    989:       *((const char **) arg) = _NXDecodeString (s->coder, s->objectZone);
                    990:       break;
                    991:       
                    992:     case '%':
                    993:       *((const char **) arg) = _NXDecodeUniqueString (s->coder);
                    994:       break;
                    995:       
                    996:     case ':':
                    997:       *((SEL *) arg) = sel_registerName (_NXDecodeSharedString (s->coder));
                    998:       break;
                    999:       
                   1000:     case '#':
                   1001:       *((Class *) arg) = NXReadClass (s);
                   1002:       break;
                   1003:       
                   1004:     case '[':
                   1005:       {
                   1006:        unsigned int size, align;
                   1007:        unsigned int i, count = 0;
                   1008:        const char *elementType;
                   1009:        
                   1010:        while ('0' <= *type && *type <= '9')
                   1011:          count = 10 * count + (*type++ - '0');
                   1012:        
                   1013:        elementType = type;
                   1014:        type = SizeOfType (elementType, &size, &align);
                   1015:        
                   1016:        for (i = 0; i < count; i++)
                   1017:          ReadValue (stream, elementType, ((char *) arg) + (i * size));
                   1018:        
                   1019:        c = *type++;
                   1020:        if (c != ']')
                   1021:          typeDescriptorError (c, "missing ']' in type descriptor");
                   1022:        
                   1023:        break;
                   1024:       }
                   1025:       
                   1026:     case '{':
                   1027:       {
                   1028:         unsigned int offset = 0;
                   1029:        
                   1030:        type = skipAggregateName (type);
                   1031:        
                   1032:        while (*type != '}')
                   1033:          {
                   1034:            unsigned int size, align;
                   1035:            
                   1036:            SizeOfType (type, &size, &align);
                   1037:            
                   1038:            offset = roundUp (offset, align);
                   1039:            
                   1040:            type = ReadValue (stream, type, ((char *) arg) + offset);
                   1041:            
                   1042:            offset += size;
                   1043:          }
                   1044:        
                   1045:        type++;
                   1046:        break;
                   1047:       }
                   1048:       
                   1049:     case '(':
                   1050:       {
                   1051:        unsigned int i, size, align;
                   1052:        
                   1053:        type = skipAggregateName (type);
                   1054:        
                   1055:        type = SizeOfType (type - 1, &size, &align);
                   1056:        
                   1057:        for (i = 0; i < size; i++)
                   1058:          ReadValue (stream, "C", ((char *) arg) + i);
                   1059:        
                   1060:        break;
                   1061:       }
                   1062:       
                   1063:     case '!': 
                   1064:       break;
                   1065:       
                   1066:     default:
                   1067:       typeDescriptorError (c, "unknown type descriptor");
                   1068:     }
                   1069:   
                   1070:   return type;
                   1071: }
                   1072: 
                   1073: /***********************************************************************
                   1074:  *     Writing and reading arbitrary data: API functions
                   1075:  **********************************************************************/
                   1076: 
                   1077: void NXWriteType (NXTypedStream *stream, const char *type, const void *data)
                   1078: {
                   1079:   TypedStream *s = (TypedStream *) stream;
                   1080:   
                   1081:   checkWrite (s);
                   1082:   _NXEncodeSharedString (s->coder, type);
                   1083:   type = WriteValue (stream, type, data);
                   1084:   if (*type)
                   1085:     typeDescriptorError (*type, "excess characters in type descriptor");
                   1086: }
                   1087: 
                   1088: void NXReadType (NXTypedStream *stream, const char *type, void *data)
                   1089: {
                   1090:   TypedStream *s = (TypedStream *) stream;
                   1091:   const char *readType;
                   1092:   
                   1093:   checkRead (s);
                   1094:   readType = _NXDecodeSharedString (s->coder);
                   1095:   checkExpected (readType, type);
                   1096:   type = ReadValue (stream, type, data);
                   1097:   if (*type)
                   1098:     typeDescriptorError (*type, "excess characters in type descriptor");
                   1099: }
                   1100:     
                   1101: // NXWriteTypes() should be changed to write the type of each component
                   1102: // before that component rather than writing all the types followed
                   1103: // by all of the components.  This way the data could be read back with
                   1104: // a series of NXReadType() calls or a single NXReadTypes() call.
                   1105: 
                   1106: void NXWriteTypes (NXTypedStream *stream, const char *type, ...)
                   1107: {
                   1108:   TypedStream *s = (TypedStream *) stream;
                   1109:   va_list ap;
                   1110:   
                   1111:   checkWrite (s);
                   1112:   _NXEncodeSharedString (s->coder, type);
                   1113:   va_start (ap, type);
                   1114:   
                   1115:   while (*type)
                   1116:     type = WriteValue (stream, type, va_arg (ap, void *));
                   1117:   
                   1118:   va_end (ap);
                   1119: }
                   1120: 
                   1121: void NXReadTypes (NXTypedStream *stream, const char *type, ...)
                   1122: {
                   1123:   TypedStream *s = (TypedStream *) stream;
                   1124:   const char *readType;
                   1125:   va_list ap;
                   1126:   
                   1127:   checkRead (s);
                   1128:   readType = _NXDecodeSharedString (s->coder);
                   1129:   checkExpected (readType, type);
                   1130:   va_start (ap, type);
                   1131:   
                   1132:   while (*type)
                   1133:     type = ReadValue (stream, type, va_arg (ap, void *));
                   1134:   
                   1135:   va_end (ap);
                   1136: }
                   1137:     
                   1138: /***********************************************************************
                   1139:  *     Conveniences for writing and reading common types of data.
                   1140:  **********************************************************************/
                   1141:  
                   1142: void NXWriteArray (NXTypedStream *stream, const char *type, int count, const void *data)
                   1143: {
                   1144:   TypedStream *s = (TypedStream *) stream;
                   1145:   char *arrayType = malloc (15 + strlen (type));       /* enough room */
                   1146:   
                   1147:   checkWrite (s);
                   1148:   sprintf (arrayType, "[%d%s]", count, type);
                   1149:   _NXEncodeSharedString (s->coder, arrayType);
                   1150:   free (arrayType);
                   1151:   
                   1152:   if (*type == 'c' && *(type+1) == 0)
                   1153:     {
                   1154:       _NXEncodeBytes (s->coder, data, count);
                   1155:       type++;
                   1156:     }
                   1157:   else
                   1158:     {
                   1159:       unsigned int size, align, i;
                   1160:       const char *elementType = type;
                   1161:       char descriptorToUse[ strlen( elementType ) + 3];
                   1162:       
                   1163:       if ( *elementType && *elementType != '{' && *(elementType+1) ) {
                   1164:         strcpy( descriptorToUse, "{" );
                   1165:         strcat( descriptorToUse, elementType );
                   1166:         strcat( descriptorToUse, "}" );
                   1167:         elementType = descriptorToUse;
                   1168:       }
                   1169:       
                   1170:       type = SizeOfType (elementType, &size, &align);
                   1171: 
                   1172:       for (i = 0; i < count; i++)
                   1173:        WriteValue (stream, elementType, ((char *) data) + (i * size));
                   1174:     }
                   1175:   
                   1176:   if (*type)
                   1177:     typeDescriptorError (*type, "excess characters in type descriptor");
                   1178: }
                   1179: 
                   1180: void NXReadArray (NXTypedStream *stream, const char *type, int count, void *data)
                   1181: {
                   1182:   TypedStream *s = (TypedStream *) stream;
                   1183:   const char *readType;
                   1184:   char *arrayType = malloc (15 + strlen (type));       /* enough room */
                   1185:   
                   1186:   checkRead (s);
                   1187:   sprintf (arrayType, "[%d%s]", count, type);
                   1188:   readType = _NXDecodeSharedString (s->coder);
                   1189:   checkExpected (readType, arrayType);
                   1190:   free (arrayType);
                   1191:   
                   1192:   if (*type == 'c' && *(type+1) == 0)
                   1193:     {
                   1194:       _NXDecodeBytes (s->coder, data, count);
                   1195:       type++;
                   1196:     }
                   1197:   else
                   1198:     {
                   1199:       unsigned int size, align, i;
                   1200:       const char *elementType = type;
                   1201:       char descriptorToUse[ strlen( elementType ) + 3];
                   1202: 
                   1203:       if ( *elementType && *elementType != '{' && *(elementType+1) ) {
                   1204:         strcpy( descriptorToUse, "{" );
                   1205:         strcat( descriptorToUse, elementType );
                   1206:         strcat( descriptorToUse, "}" );
                   1207:         elementType = descriptorToUse;
                   1208:       }
                   1209: 
                   1210:       type = SizeOfType (elementType, &size, &align);
                   1211: 
                   1212:       for (i = 0; i < count; i++)
                   1213:        ReadValue (stream, elementType, ((char *) data) + (i * size));
                   1214:     }
                   1215:   
                   1216:   if (*type)
                   1217:     typeDescriptorError (*type, "excess characters in type descriptor");
                   1218: }
                   1219: 
                   1220: /* Note, the writing of the class should be moved to [Object write].
                   1221:    This way an object can choose to write another object in its place.
                   1222:    No change was made for 3.0 since it a sensitive area and we were able
                   1223:    to work around the problem in NXConstantString.  This should definitley
                   1224:    be reexamined for 4.0!  (mself 10/8/91) */
                   1225: 
                   1226: static void InternalWriteObject (TypedStream *s, id object) {
                   1227:     if ([object respondsTo:@selector(startArchiving:)])
                   1228:        object = [object startArchiving: s];
                   1229:     if (s->noteConditionals) {
                   1230:        if (NXHashMember (s->ids, object)) return; /* already visited */
                   1231:        (void) NXHashInsert (s->ids, object);
                   1232:        };
                   1233:     if (_NXEncodeShared (s->coder, object)) {
                   1234:        int     addr = 0;
                   1235:        Class   class = object->isa;
                   1236:        NXWriteClass (s, class);
                   1237:        if (! s->noteConditionals && s->doStatistics)
                   1238:            addr = NXTell(s->coder->physical);
                   1239:        /* a misfeature in the first version */
                   1240:        if (s->streamerVersion == FIRSTSTREAMERVERSION)
                   1241:            _NXEncodeInt (s->coder, 0); 
                   1242:        [object write: s];
                   1243:        _NXEncodeChar (s->coder, EOOLABEL);
                   1244:        if (! s->noteConditionals && s->doStatistics)
                   1245:            printf ("\tWritten %s object %s: %ld bytes\n", 
                   1246:                [[object class] name], [object name], 
                   1247:                NXTell (s->coder->physical)-addr);
                   1248:        };
                   1249:     };
                   1250:     
                   1251: void NXWriteObject (NXTypedStream *stream, id object) {
                   1252:     TypedStream        *s = (TypedStream *) stream;
                   1253:     
                   1254:     checkWrite (s);
                   1255:     if (s->streamerVersion > FIRSTSTREAMERVERSION)
                   1256:        _NXEncodeSharedString (s->coder, "@");
                   1257:     InternalWriteObject (s, object);
                   1258:     };
                   1259:     
                   1260: static id InternalReadObject (TypedStream *s) {
                   1261:   id object;
                   1262:   char ch;
                   1263:   switch (ch = NXGetc(s->coder->physical)) {
                   1264:   case NULLLABEL: return NULL;
                   1265:   case NEWLABEL: {
                   1266:        Class   class;
                   1267:        int     size;
                   1268:        int label = inc_ptrCounter(s->coder);
                   1269:        class = NXReadClass (s);
                   1270:        if (s->streamerVersion == FIRSTSTREAMERVERSION)
                   1271:            size = _NXDecodeInt (s->coder);
                   1272:        if (! class) classError ("NULL", "found null class");
                   1273: #if 0
                   1274:        /* We would like to use allocFromZone: so that objects
                   1275:           may place themselves in special zones.  Before making
                   1276:           this change we must ensure that all objects which ever
                   1277:           appear in archives have a valid allocFromZone: method.  */
                   1278:        object = [class allocFromZone: s->objectZone];
                   1279: #else
                   1280:        object = class_createInstanceFromZone (class, 0, s->objectZone);
                   1281: #endif
                   1282:        PTRS(s->coder->ptrs)[label] = object;
                   1283:        [object read: s];
                   1284:        [object awake];
                   1285:        if ([object respondsTo:@selector(finishUnarchiving)]) {
                   1286:            id          new = [object finishUnarchiving];
                   1287:            if (new) {
                   1288:                object = new;
                   1289:                PTRS(s->coder->ptrs)[label] = object;
                   1290:                };
                   1291:            };
                   1292:        if (_NXDecodeChar (s->coder) != EOOLABEL) 
                   1293:            NX_RAISE (TYPEDSTREAM_FILE_INCONSISTENCY, 
                   1294:             "NXReadObject: inconsistency between written data and read:", 0); 
                   1295:        return object;
                   1296:        };
                   1297:   default: return PTRS(s->coder->ptrs)[Bias(FinishDecodeInt(s->coder, ch))];
                   1298:   }
                   1299: }
                   1300: 
                   1301: id NXReadObject (NXTypedStream *stream) {
                   1302:     const char *readType;
                   1303:     TypedStream        *s = (TypedStream *) stream;
                   1304:     
                   1305:     checkRead (s);
                   1306:     if (s->streamerVersion > FIRSTSTREAMERVERSION) {
                   1307:        readType = _NXDecodeSharedString (s->coder);
                   1308:         checkExpected (readType, "@");
                   1309:        };
                   1310:     return InternalReadObject (s);
                   1311:     };
                   1312:     
                   1313: //?? We hack around % classes (pose as?)
                   1314: static Class RealSuperClass (Class class) {
                   1315:     while ((class = class->super_class) && (class->name[0]=='%')) {};
                   1316:     return class;
                   1317:     };
                   1318:     
                   1319: static void NXWriteClass (NXTypedStream *stream, Class class) {
                   1320:     TypedStream        *s = (TypedStream *) stream;
                   1321:     
                   1322:     checkWrite (s);
                   1323:     while (_NXEncodeShared (s->coder, class)) {
                   1324:        _NXEncodeSharedString (s->coder, class->name);
                   1325:        _NXEncodeInt (s->coder, class->version);
                   1326:        //?? Hack for skipping PoseAs classes (indicated by %)
                   1327:        class = RealSuperClass (class);
                   1328:        };
                   1329:     };
                   1330: 
                   1331: static Class NXReadClass (NXTypedStream *stream) {
                   1332:     TypedStream        *s = (TypedStream *) stream;
                   1333:     char ch;
                   1334:     
                   1335:     checkRead (s);
                   1336:     switch (ch = NXGetc(s->coder->physical)) {
                   1337:     case NULLLABEL: return NULL;
                   1338:     case NEWLABEL: {
                   1339:       const char       *className = _NXDecodeSharedString (s->coder);
                   1340:       int              version = _NXDecodeInt (s->coder);
                   1341:       Class            superClass;
                   1342:       Class            class = (Class) objc_getClass ((char *) className);
                   1343:       _ClassVersion    *cv = (_ClassVersion *) 
                   1344:           NXZoneMalloc (s->coder->scratch, sizeof(_ClassVersion));
                   1345:       /* explicit class initialization */
                   1346:       (void) [(id) class self];
                   1347:        cv->className = className; cv->version = version;
                   1348:        (void) NXHashInsert (s->classVersions, cv);
                   1349:       if (! class) classError (className, "class not loaded");
                   1350:       PTRS(s->coder->ptrs)[inc_ptrCounter(s->coder)] = class;
                   1351:       superClass = NXReadClass (s);
                   1352:       if (superClass != RealSuperClass (class)) 
                   1353:        classError (className, "wrong super class");
                   1354:       return class;
                   1355:     };
                   1356:     default: return PTRS(s->coder->ptrs)[Bias(FinishDecodeInt(s->coder, ch))];
                   1357:     }
                   1358:   };
                   1359:      
                   1360: 
                   1361: /***********************************************************************
                   1362:  *     Writing and reading back pointers.
                   1363:  **********************************************************************/
                   1364: 
                   1365: void NXWriteRootObject (NXTypedStream *stream, id object) {
                   1366:     TypedStream        *s = (TypedStream *) stream;
                   1367:     _CodingStream      *olds;
                   1368:     
                   1369:     checkWrite (s);
                   1370:     if (s->noteConditionals) 
                   1371:        writeRefError ("NXWriteRootObject: already done");
                   1372:     s->noteConditionals = YES;
                   1373:     if (! s->ids) 
                   1374:        s->ids = NXCreateHashTable (NXPtrPrototype, 0, NULL);
                   1375:     olds = s->coder;
                   1376:     s->coder = _NXOpenEncodingStream ((NXStream *) nil);
                   1377:     NXWriteObject (stream, object);
                   1378:     _NXCloseCodingStream (s->coder);
                   1379:     s->coder = olds;
                   1380:     s->noteConditionals = NO;
                   1381:     NXWriteObject (stream, object);
                   1382:     NXFlushTypedStream (stream);
                   1383:     };
                   1384: 
                   1385: void NXWriteObjectReference (NXTypedStream *stream, id object) {
                   1386:     TypedStream        *s = (TypedStream *) stream;
                   1387:     
                   1388:     checkWrite (s);
                   1389:     if (s->noteConditionals) return;
                   1390:     if (! s->ids) 
                   1391:        writeRefError ("NXWriteObjectReference: NXWriteRootObject has not been previously done");
                   1392:     if (! object) {NXWriteObject (stream, nil); return;};
                   1393:     NXWriteObject (stream, (NXHashMember (s->ids, object)) ? object : nil);
                   1394:     };
                   1395: 
                   1396: /***********************************************************************
                   1397:  *     Conveniences for writing and reading files and buffers.
                   1398:  **********************************************************************/
                   1399:  
                   1400: NXTypedStream *NXOpenTypedStreamForFile (const char *fileName, int mode) {
                   1401:     TypedStream        *s;
                   1402:     NXStream           *physical;
                   1403:     int fd;
                   1404:     switch (mode) {
                   1405:        case NX_WRITEONLY: 
                   1406:             /* workaround NXMapFile bug */
                   1407:             fd = open (fileName, O_CREAT | O_WRONLY, 0666);
                   1408:             if (fd < 0) return NULL;
                   1409:             close(fd);
                   1410:            physical = NXOpenMemory (NULL, 0, NX_WRITEONLY);
                   1411:            break;
                   1412:        case NX_READONLY:
                   1413:            physical = NXMapFile (fileName, mode);
                   1414:            if (! physical) return NULL;
                   1415:            break;
                   1416:        default: NX_RAISE (TYPEDSTREAM_CALLER_ERROR, 
                   1417:                "NXOpenTypedStreamForFile: invalid mode", (void *) mode);
                   1418:        };
                   1419:    s = (TypedStream *) NXOpenTypedStream (physical, mode);
                   1420:    if (! s) return NULL;
                   1421:    s->fileName = fileName;
                   1422:    return s;
                   1423:    };
                   1424: 
                   1425: char *NXWriteRootObjectToBuffer (id object, int *length) {
                   1426:     char               *buffer;
                   1427:     int                max;
                   1428:     NXStream           *physical = NXOpenMemory(NULL, 0, NX_WRITEONLY);
                   1429:     NXTypedStream      *stream = NXOpenTypedStream (physical, NX_WRITEONLY);
                   1430:     NXWriteRootObject (stream, object);
                   1431:     NXCloseTypedStream (stream);
                   1432:     NXGetMemoryBuffer(physical, &buffer, length, &max);
                   1433:     NXCloseMemory(physical, NX_TRUNCATEBUFFER);
                   1434:     return buffer;
                   1435:     };
                   1436: 
                   1437: id NXReadObjectFromBuffer (const char *buffer, int length) {
                   1438:     return NXReadObjectFromBufferWithZone(buffer, length, NXDefaultMallocZone());
                   1439:     };
                   1440: 
                   1441: id NXReadObjectFromBufferWithZone (const char *buffer, int length, NXZone *zone) {
                   1442:     NXStream           *physical = NXOpenMemory (buffer, length, NX_READONLY);
                   1443:     NXTypedStream      *stream = NXOpenTypedStream (physical, NX_READONLY);
                   1444:     id                 object;
                   1445:     
                   1446:     if (!stream)
                   1447:       NX_RAISE (TYPEDSTREAM_CALLER_ERROR, "NXOpenTypedStream: null stream", 0);
                   1448:     NXSetTypedStreamZone(stream, zone);
                   1449:     object = NXReadObject (stream);
                   1450:     NXCloseTypedStream (stream);
                   1451:     NXCloseMemory(physical, NX_SAVEBUFFER);
                   1452:     return object;
                   1453:     };
                   1454: 
                   1455: void NXFreeObjectBuffer (char *buffer, int length) {
                   1456:     (void) vm_deallocate (task_self(), (vm_address_t) buffer, length);
                   1457:     };
                   1458: 
                   1459: 
                   1460: /***********************************************************************
                   1461:  *     Dealing with versions
                   1462:  **********************************************************************/
                   1463: 
                   1464: int NXSystemVersion (NXTypedStream *stream) {
                   1465:     TypedStream        *s = (TypedStream *) stream;
                   1466:     return s->systemVersion;
                   1467:     };
                   1468: 
                   1469: int NXTypedStreamClassVersion (NXTypedStream *stream, const char *className) {
                   1470:     _ClassVersion      pseudo;
                   1471:     _ClassVersion      *original;
                   1472:     TypedStream                *s = (TypedStream *) stream;
                   1473:     checkRead (s);
                   1474:     pseudo.className = className;
                   1475:     original = (_ClassVersion *) NXHashGet (s->classVersions, &pseudo);
                   1476:     if (!original) {
                   1477:         /* Hack to allow the renaming of the appkit classes to NXFoo's */
                   1478:        if (*className && (*className == 'N') 
                   1479:            && *(className + 1) && (*(className + 1) == 'X')
                   1480:            && *(className + 2)) {
                   1481:           pseudo.className = className + 2;
                   1482:           original = (_ClassVersion *) NXHashGet (s->classVersions, &pseudo);
                   1483:        }
                   1484:     }
                   1485:     return original ? original->version : -1;
                   1486:     };
                   1487: 
                   1488: BOOL NXTypedStreamIsSwapped (NXTypedStream *stream) {
                   1489:    TypedStream         *s = (TypedStream*) stream;
                   1490:    return s->coder->swap;
                   1491: }
                   1492: 
                   1493: #if 0
                   1494: 
                   1495: static void NXPrintTypes (NXTypedStream *stream, NXStream *output);
                   1496: 
                   1497: static char *SkipClass (NXTypedStream *stream)
                   1498: {
                   1499:   TypedStream *s = (TypedStream *) stream;
                   1500:   char ch;
                   1501:   
                   1502:   checkRead (s);
                   1503:   switch (ch = NXGetc (s->coder->physical))
                   1504:     {
                   1505:     case NULLLABEL:
                   1506:       return "Nil";
                   1507:       
                   1508:     case NEWLABEL:
                   1509:       {
                   1510:        char *className = _NXDecodeSharedString (s->coder);
                   1511:        int version = _NXDecodeInt (s->coder);
                   1512:         int label = inc_ptrCounter (s->coder);
                   1513:        
                   1514:        PTRS (s->coder->ptrs)[label] = className;
                   1515:        SkipClass (s);
                   1516:        return className;
                   1517:       }
                   1518:       
                   1519:     default:
                   1520:       {
                   1521:         int label = Bias (FinishDecodeInt (s->coder, ch));
                   1522:        char *className = PTRS (s->coder->ptrs)[label];
                   1523:        
                   1524:        return className;
                   1525:       }
                   1526:     }
                   1527: }
                   1528: 
                   1529: static void NXPrintClass (NXTypedStream *stream, NXStream *output)
                   1530: {
                   1531:   NXPrintf (output, "(%s)", SkipClass (stream));
                   1532: }
                   1533: 
                   1534: 
                   1535: static void InternalPrintObject (TypedStream *s, NXStream *output)
                   1536: {
                   1537:   char ch;
                   1538:   
                   1539:   switch (ch = NXGetc (s->coder->physical))
                   1540:     {
                   1541:     case NULLLABEL:
                   1542:       NXPrintf (output, "nil");
                   1543:       return;
                   1544:     
                   1545:     case NEWLABEL:
                   1546:       {
                   1547:         int label = inc_ptrCounter (s->coder);
                   1548:        char *className = SkipClass (s);
                   1549:        
                   1550:        NXPrintf (output, "(%s#%d = ", className, label);
                   1551:        PTRS (s->coder->ptrs)[label] = className;
                   1552:        if (s->streamerVersion == FIRSTSTREAMERVERSION)
                   1553:          _NXDecodeInt (s->coder);
                   1554:        while ((ch = NXGetc (s->coder->physical)) != EOOLABEL)
                   1555:          {
                   1556:            if (ch == NEWLABEL)
                   1557:              {
                   1558:                NXUngetc (s->coder->physical);
                   1559:                NXPrintTypes (s, output);
                   1560:              }
                   1561:            else
                   1562:              NXPrintf (output, "?");
                   1563:          }
                   1564:        NXPrintf (output, ")");
                   1565:        return;
                   1566:       }
                   1567:     default:
                   1568:       {
                   1569:         int label = Bias (FinishDecodeInt (s->coder, ch));
                   1570:        const char *className = PTRS (s->coder->ptrs)[label];
                   1571:        
                   1572:        NXPrintf (output, "(%s#d)", className, label);
                   1573:        return;
                   1574:       }
                   1575:     }
                   1576: }
                   1577: 
                   1578: 
                   1579: // Print one object described by "type".
                   1580: // Returns the unread portion of "type".
                   1581: 
                   1582: static const char *
                   1583: PrintValue (NXTypedStream *stream, const char *type, NXStream *output)
                   1584: {
                   1585:   TypedStream *s = (TypedStream *) stream;
                   1586:   char c = *type++;
                   1587:   
                   1588:   switch (c)
                   1589:     {
                   1590:     case 'c':
                   1591:       NXPrintf (output, "%d", _NXDecodeChar (s->coder));
                   1592:       break;
                   1593:       
                   1594:     case 'C':
                   1595:       NXPrintf (output, "%u", _NXDecodeChar (s->coder));
                   1596:       break;
                   1597:       
                   1598:     case 's':
                   1599:       NXPrintf (output, "%d", _NXDecodeShort (s->coder));
                   1600:       break;
                   1601:       
                   1602:     case 'S':
                   1603:       NXPrintf (output, "%u", _NXDecodeShort (s->coder));
                   1604:       break;
                   1605:       
                   1606:     case 'i':
                   1607:     case 'l':
                   1608:       NXPrintf (output, "%d", _NXDecodeInt (s->coder));
                   1609:       break;
                   1610:       
                   1611:     case 'I':
                   1612:     case 'L':
                   1613:       NXPrintf (output, "%u", _NXDecodeInt (s->coder));
                   1614:       break;
                   1615:       
                   1616:     case 'f':
                   1617:       NXPrintf (output, "%g", _NXDecodeFloat (s->coder));
                   1618:       break;
                   1619:       
                   1620:     case 'd':
                   1621:       NXPrintf (output, "%g", _NXDecodeDouble (s->coder));
                   1622:       break;
                   1623:       
                   1624:     case '@':
                   1625:       InternalPrintObject (s, output);
                   1626:       break;
                   1627:       
                   1628:     case '*':
                   1629:       NXPrintf (output, "\"%s\"", _NXDecodeString (s->coder, s->objectZone));
                   1630:       break;
                   1631:       
                   1632:     case '%':
                   1633:       NXPrintf (output, "\"%s\"", _NXDecodeUniqueString (s->coder));
                   1634:       break;
                   1635:       
                   1636:     case ':':
                   1637:       NXPrintf (output, "\"%s\"", _NXDecodeSharedString (s->coder));
                   1638:       break;
                   1639:       
                   1640:     case '#':
                   1641:       NXPrintClass (s, output);
                   1642:       break;
                   1643:       
                   1644:     case '[':
                   1645:       {
                   1646:        unsigned int size, align;
                   1647:        unsigned int i, count = 0;
                   1648:        const char *elementType;
                   1649:        
                   1650:        while ('0' <= *type && *type <= '9')
                   1651:          count = 10 * count + (*type++ - '0');
                   1652:        
                   1653:        elementType = type;
                   1654:        type = SizeOfType (elementType, &size, &align);
                   1655:        
                   1656:        NXPrintf (output, "[");
                   1657:        for (i = 0; i < count; i++)
                   1658:          {
                   1659:            PrintValue (stream, elementType, output);
                   1660:            if (i + 1 < count)
                   1661:              NXPrintf (output, ", ");
                   1662:          }
                   1663:        NXPrintf (output, "]");
                   1664:        
                   1665:        c = *type++;
                   1666:        if (c != ']')
                   1667:          typeDescriptorError (c, "missing ']' in type descriptor");
                   1668:        
                   1669:        break;
                   1670:       }
                   1671:       
                   1672:     case '{':
                   1673:       {
                   1674:         unsigned int offset = 0;
                   1675:        
                   1676:        type = skipAggregateName (type);
                   1677:        
                   1678:        NXPrintf (output, "{");
                   1679:        while (*type != '}')
                   1680:          {
                   1681:            unsigned int size, align;
                   1682:            
                   1683:            SizeOfType (type, &size, &align);
                   1684:            
                   1685:            offset = roundUp (offset, align);
                   1686:            
                   1687:            type = PrintValue (stream, type, output);
                   1688:            
                   1689:            offset += size;
                   1690:            
                   1691:            if (*type != '}')
                   1692:              NXPrintf (output, ", ");
                   1693:          }
                   1694:        NXPrintf (output, "}");
                   1695:        
                   1696:        type++;
                   1697:        break;
                   1698:       }
                   1699:       
                   1700:     case '(':
                   1701:       {
                   1702:        unsigned int i, size, align;
                   1703:        
                   1704:        type = skipAggregateName (type);
                   1705:        
                   1706:        type = SizeOfType (type - 1, &size, &align);
                   1707:        
                   1708:        NXPrintf (output, "(");
                   1709:        for (i = 0; i < size; i++)
                   1710:          {
                   1711:            PrintValue (stream, "C", output);
                   1712:            if (i + 1 < size)
                   1713:              NXPrintf (output, ", ");
                   1714:          }
                   1715:        NXPrintf (output, ")");
                   1716:        
                   1717:        break;
                   1718:       }
                   1719:       
                   1720:     case '!': 
                   1721:       break;
                   1722:       
                   1723:     default:
                   1724:       typeDescriptorError (c, "unknown type descriptor");
                   1725:     }
                   1726:   
                   1727:   return type;
                   1728: }
                   1729: 
                   1730: static void NXPrintTypes (NXTypedStream *stream, NXStream *output)
                   1731: {
                   1732:   TypedStream *s = (TypedStream *) stream;
                   1733:   const char *readType;
                   1734:   
                   1735:   checkRead (s);
                   1736:   readType = _NXDecodeSharedString (s->coder);
                   1737:   
                   1738:   while (*readType)
                   1739:     {
                   1740:       readType = PrintValue (stream, readType, output);
                   1741:       if (*readType)
                   1742:        NXPrintf (output, ", ");
                   1743:     }
                   1744: }
                   1745: 
                   1746: void main (int argc, char *argv[])
                   1747: {
                   1748:   int i;
                   1749:   NXStream *output = NXOpenFile (fileno (stdout), NX_WRITEONLY);
                   1750:   
                   1751:   for (i = 1; i < argc; i++)
                   1752:     {
                   1753:       NXTypedStream *stream = NXOpenTypedStreamForFile (argv[i], NX_READONLY);
                   1754:       
                   1755:       NXPrintTypes (stream, output);
                   1756:     }
                   1757: }
                   1758: 
                   1759: #endif
                   1760: #endif /* KERNEL */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.