1 /*
2  * $Id: binio.c,v 1.7 2010-07-03 19:42:31 dhmunro Exp $
3  * Define Yorick functions for dealing with binary I/O
4  */
5 /* Copyright (c) 2005, The Regents of the University of California.
6  * All rights reserved.
7  * This file is part of yorick (http://yorick.sourceforge.net).
8  * Read the accompanying LICENSE file for details.
9  */
10 
11 #ifdef NOT_YORICK
12 #include "binio.h"
13 #else
14 #include "ydata.h"
15 #endif
16 #include "yio.h"
17 #include "defmem.h"
18 #include "pstdlib.h"
19 #include <string.h>
20 
21 #undef NEVER_USE
22 
23 /*--------------------------------------------------------------------------*/
24 
25 /* Default raw I/O operations use fread, fwrite, ftell, fseek, and fclose */
26 extern long YReadIO(IOStream *file, void *buf, long size, long n);
27 extern void YWriteIO(IOStream *file, const void *buf, long size, long n);
28 extern long YTellIO(IOStream *file, long offset);
29 extern void YSeekIO(IOStream *file, long offset);
30 extern void YSeekEndIO(IOStream *file, long offset);
31 extern void YCloseIO(IOStream *file);
32 
33 extern void FreeMemberList(Member *member, long n);
34 
35 static void ChildOpen(HistoryInfo *history, int ifile);
36 
37 static void FreeHistoryInfo(HistoryInfo *history);
38 static int HasReferences(StructDef *base);
39 
40 extern void YErrorIO(const char *msg);
41 
42 /*--------------------------------------------------------------------------*/
43 
JumpToTime(HistoryInfo * history,double time)44 int JumpToTime(HistoryInfo *history, double time)
45 {
46   long i, n;
47   double *times, dtime=0.0, oldtime;
48 
49   if (!history) return 1;
50   n= history->nRecords;
51   times= history->time;
52   if (n<=0 || !times) return 1;
53 
54   oldtime= 0.0;
55   for (i=0 ; i<n ; i++) {
56     dtime= times[i]-time;
57     if (dtime>=0.0) break;
58     oldtime= dtime;
59   }
60   if (dtime>0.0 && i>0) { if (oldtime+dtime > 0.0) i--; }
61   else if (i>=n) i--;
62 
63   return JumpRecord(history, i);
64 }
65 
JumpToCycle(HistoryInfo * history,long ncyc)66 int JumpToCycle(HistoryInfo *history, long ncyc)
67 {
68   long i, n;
69   long *ncycs, dncyc=0, oldncyc;
70 
71   if (!history) return 1;
72   n= history->nRecords;
73   ncycs= history->ncyc;
74   if (n<=0 || !ncycs) return 1;
75 
76   oldncyc= 0;
77   for (i=0 ; i<n ; i++) {
78     dncyc= ncycs[i]-ncyc;
79     if (dncyc>=0) break;
80     oldncyc= dncyc;
81   }
82   if (dncyc>0 && i>0) { if (oldncyc+dncyc > 0) i--; }
83   else if (i>=n) i--;
84 
85   return JumpRecord(history, i);
86 }
87 
88 /* JumpRecord returns 0 on success, 1 on recNumber out of range.
89    In the latter case, there will be no current record.  */
JumpRecord(HistoryInfo * history,long recNumber)90 int JumpRecord(HistoryInfo *history, long recNumber)
91 {
92   IOStream *child;
93   if (!history) return 1;
94   child= history->child;
95   history->recNumber= -1;   /* safe against failure until actually set */
96   if (recNumber<0 || recNumber>=history->nRecords) return 1;
97   ChildOpen(history, history->ifile[recNumber]);
98   child->offset= history->offset[recNumber];
99   history->recNumber= recNumber;
100   return 0;
101 }
102 
ChildOpen(HistoryInfo * history,int ifile)103 static void ChildOpen(HistoryInfo *history, int ifile)
104 {
105   IOStream *child, *parent;
106   int fileNumber= history->fileNumber;
107   int permissions;
108 
109   if (ifile==fileNumber) return;              /* this child already open */
110   child= history->child;
111   ClearPointees(child, 0);
112   FreeClogFile(child);
113   if (fileNumber>0) child->ioOps->Close(child);
114   else FlushFile(child, 1); /* automatic with Close, necessary otherwise */
115 
116   history->recNumber= -1;      /* current record about to become invalid */
117 
118   parent= history->parent;
119   if (ifile==0) {                        /* requested file is the parent */
120     child->stream= parent->stream;
121     child->fullname= parent->fullname;
122     history->fileNumber= 0;
123     return;
124   }
125 
126   /* must open a new child */
127   permissions= child->permissions;
128   if (permissions&1) {
129     char *name= history->famNames[ifile];
130     if (permissions&2) child->stream= p_fopen(name, "r+b");
131     else child->stream= p_fopen(name, "rb");
132     if (child->stream) {
133       child->fullname= name;
134       history->fileNumber= ifile;
135     } else {
136       child->stream= parent->stream;
137       child->fullname= parent->fullname;
138       history->fileNumber= 0;
139       YErrorIO("previously opened binary history file now un-openable");
140     }
141   } else {
142     YErrorIO("attempt to seek history record in write-only binary file");
143   }
144 
145 #ifdef NEVER_USE
146   /* BUFFERING
147      This might seem like a good idea, but it turns out that both on UNIX
148      and MacIntosh (and probably DOS), "unbuffered" means to read the
149      stream one byte at a time, NOT to use the space passed to fread/fwrite
150      as the buffer, as I originally assumed.  See yio.h.  */
151   if (file) setbuf(file, (char *)0);
152 #endif
153 }
154 
155 /*--------------------------------------------------------------------------*/
156 
HasReferences(StructDef * base)157 static int HasReferences(StructDef *base)
158 {
159   if (!base->file && base->index<yStructTable.nItems &&
160       yStructList[base->index]==base) return base->references>1;
161   else return base->references>0;
162 }
163 
AddStruct(IOStream * file,const char * name,long n)164 StructDef *AddStruct(IOStream *file, const char *name, long n)
165 {
166   StructDef *base;
167 
168   if (file) {
169     if (file->history) {
170       HistoryInfo *history= file->history;
171       if (history->nRecords>1)
172         YError("illegal to add struct to file after 1st record");
173       if (file!=history->child) file= history->child;
174     }
175 
176     if (HashAdd(&file->structTable, name, n)) {
177       StructDef **structList= file->structList;
178       base= structList[hashIndex];
179       if (base->references || (base->model!=&stringStruct &&
180                                base->model!=&pointerStruct)) return 0;
181       /* redefinition of unreferenced default string or pointer
182          is the exception -- it is OK */
183       Unref(base->model);
184       base->model= 0;
185       base->dataOps= 0;
186       base->size= 0;
187       base->alignment= 0;
188       base->Convert= 0;
189       base->order= 0;
190     } else {
191       HASH_MANAGE(file->structTable, StructDef *, file->structList);
192       file->structList[hashIndex]= base= NewStructDef(file, hashIndex);
193       base->addressType= 1;
194     }
195 
196   } else {
197     if (HashAdd(&yStructTable, name, 0L)) {
198       StructDef *old= yStructList[hashIndex];
199       if (hashIndex<=8)
200         YError("attempt to replace in-memory StructDef of basic data type");
201       yStructList[hashIndex]= 0;
202       Unref(old);
203     } else {
204       HASH_MANAGE(yStructTable, StructDef *, yStructList);
205     }
206     base= NewStructDef(file/* 0 */, hashIndex);
207     yStructList[hashIndex]= Ref(base);
208   }
209 
210   return base;
211 }
212 
AlignAdjust(long address,int alignment)213 long AlignAdjust(long address, int alignment)
214 {
215   long mis= alignment>1? address%alignment : 0;
216   return mis? address+(alignment-mis) : address;
217 }
218 
219 #define N_PRIMITIVES 8
220 #define NDX_LONG 3
221 #define NDX_STRING 6
222 #define NDX_POINTER 7
223 static StructDef *primStructs[N_PRIMITIVES]= {
224   &charStruct, &shortStruct, &intStruct, &longStruct,
225   &floatStruct, &doubleStruct, &stringStruct, &pointerStruct };
226 /* never needed?
227 static char *primNames[N_PRIMITIVES]= {
228   "char", "short", "int", "long", "float", "double", "string", "pointer" };
229  */
230 
DefinePrimitive(StructDef * base,long size,int alignment,int addressType,int order,FPLayout * fpLayout,StructDef * model,Converter * Convert)231 int DefinePrimitive(StructDef *base, long size, int alignment,
232                     int addressType, int order, FPLayout *fpLayout,
233                     StructDef *model, Converter *Convert)
234 {
235   int i;
236 
237   if (HasReferences(base))
238     YError("illegal to change a previously referenced struct");
239   if (base->table.nItems)
240     YError("illegal to make a compound struct into a primitive");
241 
242   if (base->file) {
243     for (i=0 ; i<N_PRIMITIVES ; i++) if (model==primStructs[i]) break;
244     if (base->index<NDX_STRING && base->index!=i) return 1;
245     /* redefinition of char, short, int, long, float, or double */
246     else if ((i==NDX_STRING || i==NDX_POINTER) && base->index!=i &&
247              !Convert) return 2;
248     /* illegal to alias another type to string or pointer */
249   } else {
250     i= N_PRIMITIVES;
251   }
252 
253   base->size= size;
254   base->alignment= alignment;
255   base->addressType= addressType;
256   base->order= order;
257   if (size && fpLayout) {
258     FreeFPLayout(base->fpLayout);
259     base->fpLayout= MakeFPLayout(fpLayout, size);
260   }
261 
262   if (model) {
263     if (Convert)                         base->Convert= Convert;
264     else if (i==NDX_POINTER)             base->Convert= &ConvertP;
265     else if (i==NDX_STRING)              base->Convert= &ConvertQ;
266     else if (i<NDX_STRING && i>NDX_LONG)
267       base->Convert= (base->order==model->order && base->size==model->size &&
268                       SameFPLayout(base->fpLayout, model->fpLayout))?
269         0 : &ConvertF;
270     else if (i<=NDX_LONG && i>0)
271       base->Convert=
272         (base->order==model->order && base->size==model->size)? 0 : &ConvertI;
273     else if (i==0)                       base->Convert= 0;
274     else if (model->size!=size) return 3;
275     /* model size differs--require Converter */
276 
277     if (base->model!=model) {
278       Unref(base->model);
279       base->model= Ref(model);
280     }
281 
282     /* string and pointer are represented by long (address) on disk */
283     if ((i==NDX_STRING || i==NDX_POINTER) && !Convert) {
284       /* default string and pointer must always agree with long */
285       model= base->file->structList[NDX_LONG];
286       base->size= model->size;
287       base->alignment= model->alignment;
288       base->addressType= model->addressType;
289       base->order= model->order;
290 
291     } else if (i==NDX_LONG && base->index==NDX_LONG) {
292       /* any change to long must also change string and pointer */
293       IOStream *file= base->file;
294       long nStructs= file->structTable.nItems;
295       if (nStructs>NDX_STRING) {
296         model= file->structList[NDX_STRING];
297         if (model->Convert==&ConvertQ) {
298           model->size= base->size;
299           model->alignment= base->alignment;
300           model->addressType= base->addressType;
301           model->order= base->order;
302         }
303       }
304       if (nStructs>NDX_POINTER) {
305         model= file->structList[NDX_POINTER];
306         if (model->Convert==&ConvertP) {
307           model->size= base->size;
308           model->alignment= base->alignment;
309           model->addressType= base->addressType;
310           model->order= base->order;
311         }
312       }
313     }
314 
315   } else if (Convert) {
316     return 4;
317     /* Converter supplied without a model */
318 
319   } else if (order || size==1) {
320     if (fpLayout) {
321       if (i<N_PRIMITIVES) model= primStructs[i];
322       else if (size>4 || size>sizeof(float)) model= &doubleStruct;
323       else model= &floatStruct;
324       base->model= Ref(model);
325       base->Convert= &ConvertF;
326     } else {
327       if (i<N_PRIMITIVES) model= primStructs[i];
328       else if (size>2) model= &longStruct;
329       else if (size>1) model= &shortStruct;
330       else model= &charStruct;
331       base->model= Ref(model);
332       base->Convert= &ConvertI;
333     }
334 
335   } else {
336     return 5;
337     /* no model supplied for opaque primitive */
338   }
339 
340   return 0;
341 }
342 
AddMember(StructDef * base,long offset,const char * name,StructDef * memType,Dimension * dims)343 int AddMember(StructDef *base, long offset, const char *name,
344               StructDef *memType, Dimension *dims)
345 {
346   long number, index;
347   IOStream *file= base->file;
348 
349   if (base->dataOps)
350     YError("illegal to add member to a previously installed struct");
351   if (HasReferences(base))
352     YError("illegal to add member to a previously referenced struct");
353   if (!memType->dataOps)
354     YError("struct representing member type has not been installed");
355 
356   if (HashAdd(&base->table, name, 0L)) {
357     return 1;  /* signal duplicate member name */
358   } else {
359     HASH_MANAGE(base->table, Member, base->members);
360     HASH_MANAGE(base->table, long, base->offsets);
361   }
362   index= hashIndex;  /* protect against ravages of CopyStruct */
363 
364   /* fill in new member  */
365   memType= CopyStruct(file, memType);  /* allows memType to be a
366                                           memory struct as well as
367                                           a struct for this file */
368   if (!memType) return 2;
369   base->members[index].base= Ref(memType);
370   base->members[index].dims= Ref(dims);
371   base->members[index].number= number= TotalNumber(dims);
372 
373   /* NOTE-- base->size is NOT necessarily correct while the struct
374             is being built.  InstallStruct adjusts base->size to be
375             a multiple of base->alignment if necessary.  */
376   if (offset<0) {
377     /* must compute offset/alignment for the new data, which goes at
378        next available offset in base */
379     int alignment= memType->alignment;
380     base->size= AlignAdjust(base->size, alignment);
381     base->offsets[index]= base->size;
382     base->size+= memType->size*number;
383     if (alignment>base->alignment) base->alignment= alignment;
384 
385   } else {
386     /* offset has been specified, just accept it
387        and check whether this is beyond nextOffset */
388     long size= offset+memType->size*number;
389     base->offsets[index]= offset;
390     if (size > base->size) base->size= size;
391   }
392 
393   if (index==0) {
394     /* for first member, check for alignment restriction on all structs */
395     int structAlign= file? file->structAlign : yStructAlign;
396     /* PowerPC version of gcc bumps alignment of struct to sizeof(double)
397      * if first member is a scalar double (but not an array of double) */
398     if (structAlign==-2 && memType==&doubleStruct && !dims)
399       structAlign = memType->size;
400     if (base->alignment < structAlign) base->alignment= structAlign;
401 
402     /* single member struct can use Copier for first member
403        -- unless the struct alignment is so strict that it will
404           eventually force base->size > memType->size */
405     base->Copy= (base->alignment>memType->size)? &CopyS : memType->Copy;
406 
407   } else if (base->Copy!=&CopyS) {
408     /* be sure correct Copy virtual function is in place
409        -- CopyX is adequate until at least one member (possibly the first)
410           needs a more elaborate Copier */
411     if (base->Copy!=&CopyX || memType->Copy!=&CopyX) base->Copy= &CopyS;
412   }
413 
414   /* be sure correct Convert virtual function is in place --
415      this actually misses the possibility that a primitive with no
416      Converter may have a different alignment on disk */
417   if (memType->Convert) base->Convert= &ConvertS;
418 
419   /* be sure struct inherits "sequential" property from its members */
420   if (memType->addressType>1) base->addressType= 2;
421 
422   return 0;
423 }
424 
InstallStruct(StructDef * base,StructDef * model)425 void InstallStruct(StructDef *base, StructDef *model)
426 {
427   IOStream *file= base->file;
428   int align_for_size = base->alignment;
429 
430   if (base->dataOps)
431     YError("illegal to install a previously installed struct");
432   if (HasReferences(base))
433     YError("illegal to install a previously referenced struct");
434 
435   /* force size to a multiple of alignment (otherwise arrays of instances
436      would not be properly aligned) */
437   if (base->members && (file? file->structAlign : yStructAlign)==-1) {
438     /* IBM PowerPC alignment idiocy (partly rubbed off on gcc above)
439      * must round up base->size to sizeof(double) if first primitive
440      * data type is double */
441     StructDef *first = base->members[0].base;
442     while (first->members) first = first->members[0].base;
443     if (first==&doubleStruct && align_for_size<(int)first->size)
444       align_for_size = (int)first->size;
445   }
446   base->size = AlignAdjust(base->size, align_for_size);
447 
448   /* shrink-wrap the member table */
449   HashShrink(&base->table);
450 
451   if (file) {
452     /* Now is the time to find the appropriate memory model corresponding
453        to this StructDef.  Check yStructTable for this name to see if
454        any in-memory structs have the right name.  */
455     if (!model && !base->model &&
456         HashFind(&yStructTable, StructName(base), 0L)) {
457       model= yStructList[hashIndex];
458       if (!EquivStruct(base, model)) model= 0;
459     }
460 
461     if (base->model) {
462       /* Caller has already filled in base->model -- presume everything
463          else (Convert, order, fpLayout) is also correct.  */
464       model= base->model;
465 
466     } else if (model) {
467       /* An equivalent in-memory model for this StructDef already exists
468          and has the same name -- use it.  */
469       if (!base->table.nItems) {
470         DefinePrimitive(base, base->size, base->alignment,
471                         base->addressType, base->order, base->fpLayout,
472                         model, (Converter *)0);
473 
474       } else {
475         base->model= Ref(model);
476         /* AddMember will not catch on to the fact that a Converter is
477            necessary if the primitive alignments differ.  */
478         if (!base->Convert && !StructEqual(base, model))
479           base->Convert= &ConvertS;
480       }
481 
482     } else if (base->table.nItems || (base->order==0 && base->size!=1)) {
483       /* Must create a suitable in-memory model for this StructDef now.  */
484       long i, nItems= base->table.nItems;
485       char **names= base->table.names;
486       Member *members= base->members;
487       StructDef *submodel;
488 
489       /* Don't want to call AddStruct here, as this would replace an
490          existing in-memory data structure of the same name.  However,
491          if no such in-memory struct exists, go ahead and make one now.  */
492       if (HashAdd(&yStructTable, StructName(base), 0L)) {
493         base->model= model= NewStructDef((IOStream *)0, hashIndex);
494       } else {
495         HASH_MANAGE(yStructTable, StructDef *, yStructList);
496         base->model= model= NewStructDef((IOStream *)0, hashIndex);
497         yStructList[hashIndex]= Ref(model);
498       }
499 
500       for (i=0 ; i<nItems ; i++) {
501         submodel= members[i].base->model;
502         while (submodel->model) submodel= submodel->model;
503         if (AddMember(model, -1L, names[i], submodel, members[i].dims))
504           YError("bad StructDef passed to InstallStruct");
505       }
506       if (!nItems) {
507         model->size= base->size;
508         model->alignment= base->alignment;
509       }
510       InstallStruct(model, 0);
511 
512       /* AddMember will not catch on to the fact that a Converter is
513          necessary if the primitive alignments differ.  */
514       if (!base->Convert && !StructEqual(base, model))
515         base->Convert= &ConvertS;
516 
517     } else {
518       DefinePrimitive(base, base->size, base->alignment,
519                       base->addressType, base->order, base->fpLayout,
520                       (StructDef *)0, (Converter *)0);
521       model= base->model;
522     }
523 
524     base->dataOps= model->dataOps;
525 
526   } else {
527     /* even if this is a primitive, it is effectively an opaque structure */
528     base->dataOps= yOpsStruct;
529 
530     /* If the global variable corresponding to this struct is nil,
531        install it now.  */
532     if (DefInstallHook)
533       DefInstallHook(yStructTable.names[base->index], base);
534   }
535 }
536 
EquivStruct(StructDef * fil,StructDef * mem)537 int EquivStruct(StructDef *fil, StructDef *mem)
538 {
539   StructDef *model= fil->model;
540   if (model) {
541     while(model->model) model= model->model;
542     return (model==mem);
543 
544   } else {
545     long nItems= fil->table.nItems;
546     if (mem->table.nItems!=nItems) return 0;
547     if (nItems<=0) {
548       if ((fil->fpLayout!=0)^(mem->fpLayout!=0)) return 0;
549       if (fil->size==1 && mem->size==1) return 1;
550       if ((fil->order!=0)^(mem->order!=0)) return 0;
551       return 1;
552 
553     } else {
554       long i;
555       Member *fm= fil->members;
556       Member *mm= mem->members;
557       Dimension *fd, *md;
558       for (i=0 ; i<nItems ; i++) {
559         if (!EquivStruct(fm[i].base, mm[i].base)) return 0;
560         fd= fm[i].dims;
561         md= mm[i].dims;
562         while (fd && md) {
563           if (fd->number != md->number) return 0;
564           fd= fd->next;
565           md= md->next;
566         }
567         if (fd || md) return 0;
568       }
569       return 1;
570     }
571   }
572 }
573 
AddVariable(IOStream * file,long address,const char * name,StructDef * varType,Dimension * dims)574 int AddVariable(IOStream *file, long address, const char *name,
575                 StructDef *varType, Dimension *dims)
576 {
577   long number, index;
578   HistoryInfo *history= file->history;
579 
580   if (history) {
581     if (file!=history->child) file= history->child;
582     if (HashFind(&file->dataTable, name, 0L))
583       return 1;  /* signal duplicate data name */
584     if (history->nRecords>1)
585       YError("illegal to add record variable to file after 1st record");
586     else if (history->nRecords>0 && history->recNumber>=0 &&
587              file->nextAddress-file->offset > history->recordSize)
588       YError("non-contiguous record: be careful with pointers/strings?");
589   }
590 
591   if (!varType->dataOps)
592     YError("StructDef representing variable type has not been installed");
593 
594   if (HashAdd(&file->dataTable, name, 0L)) {
595     return 1;  /* signal duplicate data name */
596   } else {
597     HASH_MANAGE(file->dataTable, Member, file->types);
598     HASH_MANAGE(file->dataTable, long, file->addresses);
599   }
600   index= hashIndex;  /* protect against ravages of CopyStruct */
601 
602   /* fill in new member  */
603   varType= CopyStruct(file, varType);  /* allows varType to be a
604                                           memory struct as well as
605                                           a struct for this file */
606   if (!varType) return 2;
607   file->types[index].base= Ref(varType);
608   file->types[index].dims= Ref(dims);
609   file->types[index].number= number= TotalNumber(dims);
610 
611   if (address<0) {
612     /* must compute address/alignment for the new data, which goes at
613        next available address in file */
614     long next= AlignAdjust(history? history->recordSize : file->nextAddress,
615                            file->dataAlign? file->dataAlign :
616                                             varType->alignment);
617     file->addresses[index]= next;
618     next+= varType->size*number;
619     if (history) history->recordSize= next;
620     else file->nextAddress= next;
621 
622   } else {
623     /* address has been specified, just accept it
624        and check whether this is beyond nextAddress */
625     long next= address+varType->size*number;
626     file->addresses[index]= address;
627     if (history) {
628       if (next > history->recordSize) history->recordSize= next;
629     } else {
630       if (next > file->nextAddress) file->nextAddress= next;
631     }
632   }
633 
634   hashIndex= index;
635   return 0;
636 }
637 
AddHistory(IOStream * file,long size)638 HistoryInfo *AddHistory(IOStream *file, long size)
639 {
640   IOStream *child;
641   HistoryInfo *history;
642   long nStructs, i;
643   StructDef **structList, *base;
644 
645   if (y_vopen_file(file->stream))
646     YError("vopen binary file handles do not support history records");
647 
648   history= p_malloc(sizeof(HistoryInfo));
649   history->parent= file;
650   history->child= 0;  /* temporary */
651   history->nFamily= 1;
652   history->famNames= p_malloc(sizeof(char *)*4);
653   history->famNames[0]= file->fullname;  /* never free this one */
654   history->fileNumber= 0;
655   history->fileSize= size>1023? size : DEFAULT_FILE_SIZE;
656   history->copyParent= 1;   /* default is conservative? */
657 
658   history->recordSize= 0;
659   history->recordAlign= file->dataAlign;
660   history->nRecords= 0;
661   history->recNumber= -1;
662 
663   history->ifile= 0;
664   history->offset= 0;
665   history->time= 0;
666   history->ncyc= 0;
667 
668   file->history= history;
669   history->child= child=
670     NewIOStream(file->fullname, (void *)0, file->permissions);
671 
672   child->stream= file->stream;
673   child->ioOps= file->ioOps;
674   child->blockSize= file->blockSize;
675   child->structAlign= file->structAlign;
676   child->dataAlign= file->dataAlign;
677 
678   child->CloseHook= file->CloseHook;
679   child->contentsLog= file->contentsLog;
680 
681   structList= file->structList;
682   for (i=0 ; i<N_PRIMITIVES ; i++) {
683     base= child->structList[i];
684     base->size= structList[i]->size;
685     base->alignment= structList[i]->alignment;
686     base->addressType= structList[i]->addressType;
687     Unref(base->model);
688     base->model= Ref(structList[i]->model);
689     base->Convert= structList[i]->Convert;
690     base->order= structList[i]->order;
691     base->fpLayout= Ref(structList[i]->fpLayout);
692   }
693   nStructs= file->structTable.nItems;
694   for ( ; i<nStructs ; i++) CopyStruct(child, structList[i]);
695 
696   RemoveIOLink(yBinaryFiles, child);
697   return child->history= history;
698 }
699 
AddRecord(HistoryInfo * history,int flags,double time,long ncyc,long address)700 int AddRecord(HistoryInfo *history,
701               int flags, double time, long ncyc, long address)
702 {
703   long nRecords= history->nRecords;
704   IOStream *child= history->child;
705   int alignment= history->recordAlign;
706 
707   if (nRecords>0) {
708     /* all records must agree on whether time and ncyc present */
709     if (((history->time!=0)^((flags&1)!=0)) ||
710         ((history->ncyc!=0)^((flags&2)!=0))) return 1;
711 
712   } else {
713     if (alignment<=0) {
714       /* struct-like alignment is difficult for records, since most
715          restrictive alignment information has not been kept --
716          Indeed, the child->dataTable may not yet have been generated
717          at the time the first record is declared (now).
718          The best we can do is to assume the most restrictive alignment
719          of any StructDef which has already been defined.
720          A "custom" struct definition with more restrictive alignment
721          may not follow this call, and a specification of record alignment
722          which is less restrictive that the following guess must be done
723          as a special case by setting child->dataAlign before calling
724          this routine.  */
725       long i, n= child->structTable.nItems;
726       StructDef **structList= child->structList;
727       for (i=0 ; i<n ; i++)
728         if (structList[i]->alignment>alignment)
729           alignment= structList[i]->alignment;
730       history->recordAlign= alignment;
731     }
732 
733     /* Adding this first record "locks" the parent -- that is,
734        parent->nextAddress may never change afterwards.  It also sets
735        the initial child->offset.  */
736     child->nextAddress= child->offset= history->parent->nextAddress;
737   }
738 
739   if ((nRecords&0xf)==0) {
740     long newRecs= nRecords+16;
741     history->ifile= p_realloc(history->ifile, sizeof(int)*newRecs);
742     history->offset= p_realloc(history->offset, sizeof(long)*newRecs);
743     if (flags&1)
744       history->time= p_realloc(history->time, sizeof(double)*newRecs);
745     if (flags&2)
746       history->ncyc= p_realloc(history->ncyc, sizeof(long)*newRecs);
747   }
748 
749   if (address<0) {
750     /* Create new file for this record if at least one record has been
751        written to this file, and the new record would push the file
752        over the fileSize limit.  */
753     long after;
754     if (history->fileNumber != history->nFamily-1)
755       ChildOpen(history, history->nFamily-1);
756     address= AlignAdjust(child->nextAddress, alignment);
757     after= address + history->recordSize;
758     if (nRecords>0 && after>history->fileSize &&
759         history->ifile[nRecords-1]==history->fileNumber) {
760       if (AddNextFile(history, (char *)0, 1))
761         YErrorIO("failed to create next member of file family");
762       address= child->nextAddress;
763       after= address + history->recordSize;
764     } else if (nRecords>0 && (child->permissions&2)) {
765       /* dont go incredibly long without flushing history files */
766       if (history->recordSize>=8192 ||
767           !(nRecords % (1 + 8192/(history->recordSize+1))))
768         FlushFile(child, 0);
769     }
770     child->nextAddress= after;
771 
772   } else if (address+history->recordSize > child->nextAddress) {
773     /* This nextAddress is actually a minimum if the record contains
774        pointers, strings, or other indirections.  */
775     child->nextAddress= address+history->recordSize;
776   }
777 
778   /* After the first record has been added, YcWrite denies write access
779      to the non-record (parent) part of the file.  Be sure to clear out
780      any pending writes now to avoid two sets of write-armed cache blocks
781      for the same file.  */
782   if (!nRecords) FlushFile(history->parent, 1);
783 
784   history->ifile[nRecords]= history->fileNumber;
785   history->offset[nRecords]= address;
786   if (flags&1) history->time[nRecords]= time;
787   if (flags&2) history->ncyc[nRecords]= ncyc;
788   history->recNumber= nRecords;
789   history->nRecords= nRecords+1;
790   history->child->offset= address;
791 
792   return 0;
793 }
794 
795 /*--------------------------------------------------------------------------*/
796 
797 IOOperations defaultIOops= {
798   &YReadIO, &YWriteIO, &YTellIO, &YSeekIO, &YSeekEndIO, &YCloseIO
799 };
800 
801 /* Set up a block allocator which grabs space for 16 IOStream objects
802    at a time.  Since IOStream contains several pointers, the alignment
803    of an IOStream must be at least as strict as a void*.  */
804 static MemryBlock iosBlock= {0, 0, sizeof(IOStream),
805                                  16*sizeof(IOStream)};
806 
807 IOFileLink *yBinaryFiles= 0;
808 long y_block_size_0 = 0x3fff;  /* must be 2^n-1, see Y_default_blocksize */
809 
NewIOStream(char * fullname,void * stream,int permissions)810 IOStream *NewIOStream(char *fullname, void *stream, int permissions)
811 {
812   IOStream *ios= NextUnit(&iosBlock);
813   int i;
814 
815   ios->references= 0;
816   ios->ops= yOpsStream;
817   ios->stream= stream;
818   ios->fullname= fullname;
819   ios->permissions= permissions;
820   ios->ioOps= &defaultIOops;
821 
822   ios->blockSize= y_block_size_0;
823   ios->blockList= 0;
824   ios->seqAddress= 0;
825 
826   ios->structAlign= yStructAlign;
827   ios->dataAlign= 0;
828 
829   HashInit(&ios->structTable, N_PRIMITIVES);
830   ios->structList= p_malloc(sizeof(StructDef *)*ios->structTable.maxItems);
831 
832   ios->dataTable.maxItems= ios->dataTable.nSlots= ios->dataTable.nItems= 0;
833   ios->dataTable.items= 0;
834   ios->dataTable.names= 0;
835   ios->types= 0;
836   ios->addresses= 0;
837   ios->nextAddress= 0;
838   ios->offset= 0;
839 
840   ios->history= 0;
841   ios->contentsLog= 0;
842 
843   ios->pointeeList.writing= 0;
844   ios->pointeeList.nValid= ios->pointeeList.table.nItems=
845     ios->pointeeList.table.maxItems= ios->pointeeList.table.nSlots= 0;
846   ios->pointeeList.table.items= 0;
847   ios->pointeeList.mdInfo= 0;
848 
849   ios->CloseHook= 0;
850 
851   /* All binary files have definitions for the 8 primitive types.  */
852   for (i=0 ; i<N_PRIMITIVES ; i++) CopyStruct(ios, primStructs[i]);
853 
854   AddIOLink(&yBinaryFiles, ios);
855 
856   /* Do not initialize contents log here -- otherwise file is opened twice
857      when history child is created.  */
858   /* if (permissions&2) CLupdate(ios); */
859   return ios;
860 }
861 
862 /* deadFile is a bogus IOStream with 0 stream pointer to cause error
863    if read or write is attempted.  When an IOStream is freed, any of
864    its StructDefs which has outstanding references is set to point to
865    deadFile.  */
866 static IOStream deadFile;
867 
FreeIOStream(void * ios)868 void FreeIOStream(void *ios)
869 {
870   IOStream *io= ios;
871   p_file *stream= io->stream;
872   Member *types= io->types;
873   StructDef *base, **structList= io->structList;
874   HistoryInfo *history= io->history;
875   long i, nItems;
876   IOStream *parent= 0;
877 
878   /* clear out any pointees to assure that all data has been written */
879   ClearPointees(io, 0);
880 
881   /* Recurse to free any history child.
882      Note that CloseHook will be called on child if child is present
883           -- AddNextFile should have called CloseHook on parent.  */
884   if (history) {
885     IOStream *child= history->child;
886     if (child!=io) {
887       /* this is the parent -- recurse to free child */
888       Unref(child);
889       FreeHistoryInfo(history);
890 
891     } else {
892       /* this is the child -- don't close file if same as parent */
893       parent= history->parent;
894       if (io->fullname==parent->fullname) io->fullname= 0;
895       if (stream==parent->stream) stream= 0;
896       if (io->CloseHook) {
897         io->CloseHook(io);
898         io->CloseHook = parent->CloseHook = 0;
899       }
900       /* if io->ioOps->Close will be called for parent only, flush child */
901       if (!stream && io->stream) FlushFile(io, 1);
902     }
903     io->history= 0;
904 
905   } else {
906     if (io->CloseHook) {
907       io->CloseHook(io);
908       io->CloseHook = 0;
909     }
910   }
911 
912   /* closing the file discards any cache blocks */
913   if (stream) io->ioOps->Close(io);
914   io->stream= 0;
915 
916   /* contents log is a serious problem -- call to ioOps->Close above
917      potentially creates a contents log during flush if there wasn't
918      one, and parent->contentsLog is either 0 or child->contentsLog
919      depending on whether the file has been familied
920      want to call FreeClogFile for parent only, unless there are
921      separate contents logs for parent and child (impossible currently?) */
922   if (parent && !parent->contentsLog)
923     parent->contentsLog= io->contentsLog;
924   if (!parent || parent->contentsLog!=io->contentsLog)
925     FreeClogFile(io);
926 
927   p_free(io->fullname);
928   io->fullname= 0;
929 
930   /* free dataTable first to clear references to structures */
931   FreeMemberList(types, io->dataTable.nItems);
932   HashClear(&io->dataTable);
933   p_free(types);
934   io->types= 0;
935   p_free(io->addresses);
936   io->addresses= 0;
937 
938   /* free structure table in reverse order to clear dependencies */
939   nItems= io->structTable.nItems;
940   for (i=nItems-1 ; i>=0 ; i--) {
941     base= structList[i];
942     structList[i]= 0;
943     if (base->references) {
944       if (!deadFile.ops) {
945         deadFile.ops= yOpsStream;
946         deadFile.ioOps= &defaultIOops;
947         deadFile.blockSize= 0x3fff;  /* why? */
948         deadFile.structAlign= 1;     /* why? */
949       }
950       base->file= RefNC(&deadFile);
951     }
952     Unref(base);
953   }
954   HashClear(&io->structTable);
955   p_free(structList);
956 
957   RemoveIOLink(yBinaryFiles, io);
958   FreeUnit(&iosBlock, io);
959 }
960 
961 /*--------------------------------------------------------------------------*/
962 
FreeHistoryInfo(HistoryInfo * history)963 static void FreeHistoryInfo(HistoryInfo *history)
964 {
965   long i, n= history->nFamily;
966   char **famNames= history->famNames;
967 
968   history->famNames= 0;
969   for (i=1 ; i<n ; i++)
970     if (i!=history->fileNumber) p_free(famNames[i]);
971   p_free(famNames);
972 
973   p_free(history->ifile);
974   history->ifile= 0;
975   p_free(history->offset);
976   history->offset= 0;
977   p_free(history->time);
978   history->time= 0;
979   p_free(history->ncyc);
980   history->ncyc= 0;
981 
982   p_free(history);
983 }
984 
985 /*--------------------------------------------------------------------------*/
986 
987 /* Note-- filename is copied.  */
AddNextFile(HistoryInfo * history,char * filename,int create)988 int AddNextFile(HistoryInfo *history, char *filename, int create)
989 {
990   IOStream *file= history->child;
991   int n= history->nFamily;
992   void *stream;
993 
994   if (!filename) {
995     filename= NextFileName(history->famNames[history->nFamily-1]);
996   } else {
997     filename= YExpandName(filename);
998   }
999 
1000   if (!create) {
1001     if (!file->permissions&1) stream= 0;
1002     else if (file->permissions&2) stream= p_fopen(filename, "r+b");
1003     else stream= p_fopen(filename, "rb");
1004 
1005   } else {
1006     if (!(file->permissions&2)) stream= 0;
1007     else if (file->permissions&1) stream= p_fopen(filename, "w+b");
1008     else stream= p_fopen(filename, "wb");
1009   }
1010 
1011   if (stream) {
1012     p_fclose(stream);  /* will reopen it momentarily */
1013   } else {
1014     p_free(filename);
1015     return 1;
1016   }
1017 
1018   /* run any CloseHook on old file, then re-open new file */
1019   ClearPointees(file, 0);
1020   if (file->CloseHook) file->CloseHook(file);
1021 
1022   /* add new filename to history list */
1023   if (!(n&3))
1024     history->famNames= p_realloc(history->famNames, sizeof(char *)*(n+4));
1025   history->famNames[n]= filename;
1026   history->nFamily++;
1027   ChildOpen(history, n);
1028 
1029   /* optionally copy static part of file to the new file */
1030   if (history->copyParent) {
1031     IOStream *parent= history->parent;
1032     file->nextAddress= parent->nextAddress;
1033     if (create && YCopyFile(file, parent))
1034       YErrorIO("failed to copy non-record data to new file family member");
1035   } else {
1036     file->nextAddress= 0;
1037   }
1038 
1039   /* create Contents Log for new file if necessary */
1040   if (file->permissions&64) CLupdate(file);
1041 
1042   return 0;
1043 }
1044 
1045 /* Return value must be freed with p_free.
1046    Algorithm:
1047    (1) Strip directory (if any) from name.
1048    (2) If final character is a digit (0-9), it becomes increment character,
1049        else the character before the final dot (.) is the increment
1050             character (or the final character if there is no dot).
1051    (3) If the increment character is not A-Z, a-z, or 0-9, scan backwards
1052        through the name until a character in this range is found.
1053    (4) Loop:
1054          If the increment character is before the beginning of name,
1055             or if it is not in the range A-Z, a-z, or 0-9, insert the
1056             character "A" and break out of this loop.
1057          If it is 9 or z or Z, it becomes 0,
1058          else increment it and break out of this loop.
1059          Back up so the increment character becomes the previous character.
1060  */
1061 static char *headName= 0, *tailName= 0;
NextFileName(const char * name)1062 char *NextFileName(const char *name)
1063 {
1064   char *nextName;
1065   unsigned char c;
1066   long len, i, j, lenN;
1067   p_free(headName);
1068   p_free(tailName);
1069   headName= YNameHead(name);   /* directory name (if any) */
1070   tailName= YNameTail(name);   /* file name (if any) */
1071 
1072   len= tailName? strlen(tailName) : 0;
1073   c= len? tailName[len-1] : '0';
1074 
1075   /* find index i of increment character */
1076   if (c>='0' && c<='9') {
1077     i= len-1;
1078   } else {
1079     for (i=len-1 ; i>0 ; i--) if (tailName[i]=='.') break;
1080     /* i>0 if and only if there is a character before last dot */
1081     if (i>0) {
1082       c= tailName[--i];
1083       while (i>=0 && ((c<'0'||c>'9') && (c<'a'||c>'z') && (c<'A'||c>'Z')))
1084         c= tailName[--i];
1085     } else {
1086       i= len-1;
1087     }
1088   }
1089 
1090   nextName= p_strncat(name, " ", 0);  /* leave a blank just in case... */
1091   lenN= strlen(name);
1092   nextName[lenN]= '\0';
1093   j= i + lenN-len;
1094   for (;;) {
1095     if (i<0 || (((c=tailName[i])<'0'||c>'9') &&
1096                 (c<'a'||c>'z') && (c<'A'||c>'Z'))) {
1097       /* out of decent characters to increment, insert one */
1098       for (i=lenN ; i>j ; i--) nextName[i+1]= nextName[i];
1099       nextName[j+1]= 'A';
1100       break;
1101     }
1102     if (c=='9' || c=='z' || c=='Z') nextName[j]= '0';  /* need to carry */
1103     else { nextName[j]++; break; }                     /* usual case */
1104     i--;
1105     j--;
1106   }
1107 
1108   {
1109     char *tmp= headName;
1110     headName= 0;
1111     p_free(tmp);
1112     tmp= tailName;
1113     tailName= 0;
1114     p_free(tmp);
1115   }
1116   return nextName;
1117 }
1118 
1119 /*--------------------------------------------------------------------------*/
1120 
StructName(StructDef * base)1121 char *StructName(StructDef *base)
1122 {
1123   IOStream *file;
1124   if (!base) return 0;
1125   file= base->file;
1126   if (file) return file->structTable.names[base->index];
1127   else return yStructTable.names[base->index];
1128 }
1129 
1130 /* CopyStruct returns an equivalent to the input StructDef for the
1131    input IOStream.  If the StructDef already belongs to the IOStream,
1132    it is returned.  Otherwise, if the name of the StructDef already
1133    exists, CopyStruct ensures that the existing structure is equivalent
1134    to the input structure.  If so, the equivalent StructDef is returned,
1135    if not, 0 is returned (this is the only reason CopyStruct fails).
1136    If the name has never been seen before, CopyStruct attempts to
1137    build an equivalent data structure, recursing as necessary.
1138    If file==0, CopyStruct returns base->model->model->...  */
CopyStruct(IOStream * file,StructDef * base)1139 StructDef *CopyStruct(IOStream *file, StructDef *base)
1140 {
1141   StructDef *copy;
1142   long i, n;
1143   IOStream *baseFile= base->file;
1144   int verbatim;
1145   char **names;
1146   Member *members;
1147 
1148   if (file && baseFile==file) return base;
1149 
1150   /* reduce base to its ultimate in-memory model structure */
1151   verbatim= (baseFile && baseFile->history && baseFile->history->child==file);
1152   if (!verbatim) while (base->model) base= base->model;
1153 
1154   if (!file) return base;
1155 
1156   if (HashFind(&file->structTable, StructName(base), 0L)) {
1157     /* structure of this name already exists, check for equivalence
1158        -- this branch always halts following recursion as well */
1159     copy= file->structList[hashIndex];
1160     if (verbatim) return copy;
1161     while (base->model) base= base->model;
1162     return EquivStruct(copy, base)? copy : 0;
1163   }
1164 
1165   /* copy the member table -- first ensure that all structs referenced
1166      by any members have been added to file->structTable */
1167   names= base->table.names;
1168   members= base->members;
1169   n= base->table.nItems;
1170   for (i=0 ; i<n ; i++) CopyStruct(file, members[i].base);
1171   /* it is finally safe to add the struct copy to file->structTable... */
1172   copy= AddStruct(file, StructName(base), 0L);
1173   /* ...and fill in the members in the copy */
1174   for (i=0 ; i<n ; i++) {
1175     AddMember(copy, verbatim? base->offsets[i] : -1L,
1176               names[i],
1177               CopyStruct(file, members[i].base),
1178               members[i].dims);
1179   }
1180 
1181   if (verbatim) {
1182     copy->size= base->size;
1183     copy->alignment= base->alignment;
1184     copy->Copy= base->Copy;
1185     copy->addressType= base->addressType;
1186     copy->model= Ref(base->model);
1187     copy->Convert= base->Convert;
1188     copy->order= base->order;
1189     copy->fpLayout= Ref(base->fpLayout);
1190     copy->dataOps= base->dataOps;
1191 
1192   } else {
1193     if (n==0) {
1194       copy->size= base->size;
1195       copy->alignment= base->alignment;
1196       copy->order= base->order;
1197       copy->fpLayout= Ref(base->fpLayout);
1198     }
1199     InstallStruct(copy, base);
1200   }
1201 
1202   return copy;
1203 }
1204 
1205 /*--------------------------------------------------------------------------*/
1206 
YTellIO(IOStream * file,long offset)1207 long YTellIO(IOStream *file, long offset)
1208 {
1209   p_file *stream= file->stream;
1210   if (stream) {
1211     long pos= p_ftell(stream);
1212     if (pos<0) {
1213       p_ferror(stream);
1214       YErrorIO("ftell failed in YTellIO (binary file I/O)");
1215     }
1216     return pos;
1217   } else {
1218     YErrorIO("attempt to query position in closed binary file");
1219     return -1L;
1220   }
1221 }
1222 
YSeekIO(IOStream * file,long offset)1223 void YSeekIO(IOStream *file, long offset)
1224 {
1225   p_file *stream= file->stream;
1226   if (stream) {
1227     if (p_fseek(stream, offset)) {
1228       p_ferror(stream);
1229       YErrorIO("fseek failed in YSeekIO (binary file I/O)");
1230     }
1231   } else {
1232     YErrorIO("attempt to seek position in closed binary file");
1233   }
1234 }
1235 
YSeekEndIO(IOStream * file,long offset)1236 void YSeekEndIO(IOStream *file, long offset)
1237 {
1238   p_file *stream= file->stream;
1239   if (stream) {
1240     if (p_fseek(stream, p_fsize(stream)+offset)) {
1241       p_ferror(stream);
1242       YErrorIO("fseek failed in YSeekEndIO (binary file I/O)");
1243     }
1244   } else {
1245     YErrorIO("attempt to seek position in closed binary file");
1246   }
1247 }
1248 
YCloseIO(IOStream * file)1249 void YCloseIO(IOStream *file)
1250 {
1251   if (file->stream) {
1252     FlushFile(file, 1);
1253     p_fclose(file->stream);
1254     file->stream= 0;
1255   }
1256 }
1257 
1258 /*--------------------------------------------------------------------------*/
1259 
YReadIO(IOStream * file,void * buf,long size,long n)1260 long YReadIO(IOStream *file, void *buf, long size, long n)
1261 {
1262   p_file *stream= file->stream;
1263   /* can't detect permissions here because write might require this read */
1264   if (stream) {
1265     long nbytes= n*size;
1266     long nio= p_fread(stream, buf, nbytes);
1267     if (nio<nbytes) {
1268       if (p_ferror(stream)) {
1269         YErrorIO("fread failed in YReadIO (binary file I/O)");
1270       } else {
1271         nio/= size;
1272       }
1273     } else {
1274       nio= n;
1275     }
1276     return nio;
1277   } else {
1278     YErrorIO("attempt to read from closed binary file");
1279     return -1L;
1280   }
1281 }
1282 
YWriteIO(IOStream * file,const void * buf,long size,long n)1283 void YWriteIO(IOStream *file, const void *buf, long size, long n)
1284 {
1285   p_file *stream= file->stream;
1286   if (!(file->permissions & 2))
1287     YErrorIO("attempt to write to binary file opened in r mode");
1288   if (stream) {
1289     long nbytes= n*size;
1290     long nio= p_fwrite(stream, buf, nbytes);
1291     if (nio<nbytes) {
1292       p_ferror(stream);
1293       YErrorIO("fwrite failed in YWriteIO (binary file I/O)");
1294     }
1295   } else {
1296     YErrorIO("attempt to write to closed binary file");
1297   }
1298 }
1299 
1300 /*--------------------------------------------------------------------------*/
1301