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