1 /*
2  * $Id: yrdwr.c,v 1.1 2005-09-18 22:04:16 dhmunro Exp $
3  * Implement YRead and YWrite functions, plus general gather/scatter
4  * routines with StructDef format Converter.
5  */
6 /* Copyright (c) 2005, The Regents of the University of California.
7  * All rights reserved.
8  * This file is part of yorick (http://yorick.sourceforge.net).
9  * Read the accompanying LICENSE file for details.
10  */
11 
12 #include "bcast.h"
13 #include "pstdlib.h"
14 
15 /* Things to note:
16    (1) If base->file!=0, then base->model!=0 (since the in-memory
17        version of the StructDef can't refer to any particular file).
18    (2) The Convert operator will be 0 for non-pointer,
19        native-format disk files, since no conversion operation
20        is required.
21  */
22 
23 extern void ReadGather(void *dst, void *srcM, long srcD, StructDef *base,
24                        long number, const Strider *strider);
25 extern void WriteScatter(void *src, void *dstM, long dstD, StructDef *base,
26                          long number, const Strider *strider);
27 
28 static void *WriteRecurse(void *src, StructDef *base, long number);
29 extern void SetSequentialWrite(IOStream *file, long last);
30 
31 static void ClearTmp(Array **tmpArray);
32 static StructDef *NeedsConversion(StructDef *base);
33 
34 /*--------------------------------------------------------------------------*/
35 
36 /* YRead and YWrite assume that the buffer passed to them is to hold
37    the data in its FINAL format, that is, as base->model->...->model.  */
38 
YRead(void * dst,long src,StructDef * base,long number,const Strider * strider)39 void YRead(void *dst, long src, StructDef *base, long number,
40            const Strider *strider)
41 {
42   if (base->file && base->addressType==2 && strider)
43     YError("cannot use a strider reading sequential object from file");
44   ReadGather(dst, (void *)0, src, base, number, strider);
45 }
46 
YWrite(void * src,long dst,StructDef * base,long number,const Strider * strider)47 void YWrite(void *src, long dst, StructDef *base, long number,
48             const Strider *strider)
49 {
50   if (base->file && base->addressType==2) {
51     if (strider)
52       YError("cannot use a strider writing sequential object to file");
53     SetSequentialWrite(base->file, dst+base->size*number);
54   }
55   WriteScatter(src, (void *)0, dst, base, number, strider);
56 }
57 
SetSequentialWrite(IOStream * file,long last)58 void SetSequentialWrite(IOStream *file, long last)
59 {
60   if (last<file->nextAddress)
61     YError("sequential object must be written at end of binary file");
62   /* Converter for sequential object may need to know address at
63      end of object being written, since the corresponding write
64      occurs AFTER the Converter is called.  */
65   file->seqAddress= last;
66 }
67 
68 /*--------------------------------------------------------------------------*/
69 
70 static Array *tmp1Array= 0, *tmp2Array= 0;
71 
ClearTmpArray(void)72 void ClearTmpArray(void)
73 {
74   ClearTmp(&tmp1Array);
75   ClearTmp(&tmp2Array);
76 }
77 
NewTmpArray(StructDef * base,Dimension * dims)78 Array *NewTmpArray(StructDef *base, Dimension *dims)
79 {
80   Array *array= NewArray(base, dims);
81   if (tmp1Array) {
82     if (tmp2Array) {   /* both tmp1 and tmp2 exist -- deallocate tmp1 */
83       Array *tmp= tmp2Array;
84       ClearTmp(&tmp1Array);
85       tmp2Array= array;
86       tmp1Array= tmp;
87     } else {           /* only tmp1 exists, leave it alone */
88       tmp2Array= array;
89     }
90   } else {             /* neither tmp1 nor tmp2 exist, use tmp1 */
91     tmp1Array= array;
92   }
93   return array;
94 }
95 
ClearTmp(Array ** tmpArray)96 static void ClearTmp(Array **tmpArray)
97 {
98   Array *array= *tmpArray;
99   *tmpArray= 0;
100   Unref(array);
101 }
102 
NeedsConversion(StructDef * base)103 static StructDef *NeedsConversion(StructDef *base)
104 {
105   while (base) {
106     if (base->Convert) return base;
107     base= base->model;
108   }
109   return 0;
110 }
111 
112 /*--------------------------------------------------------------------------*/
113 
114 /* In a read/gather operation, base describes the data type of the
115    source (which is either in memory or on disk).  The destination is
116    assumed to have been allocated prior to this call, and to have the
117    data type base->model->...->model.
118    If base->file, then srcM==0 will cause a subsequent call to
119    ReadPointees, while if srcM!=0, the call will not be made
120    (this is intended to prevent recursion).  */
ReadGather(void * dst,void * srcM,long srcD,StructDef * base,long number,const Strider * strider)121 void ReadGather(void *dst, void *srcM, long srcD, StructDef *base,
122                 long number, const Strider *strider)
123 {
124   StructDef *preModel= NeedsConversion(base);
125   void *dstM;
126   int doPointees= (srcM==0);
127 
128   if (preModel) {
129     Array *array;
130     Dimension *dims;
131     ClearTmpArray();
132     dims= NewDimension(number, 1L, (Dimension *)0);
133     array= NewTmpArray(preModel, dims);
134     dims->references--;
135     dstM= array->value.c;
136   } else {
137     dstM= dst;
138   }
139 
140   if (base->file) CastRead(dstM, srcD, base, number, strider);
141   else Gather(dstM, srcM, base, number, strider);
142 
143   if (preModel) {
144     StructDef *model= preModel->model;
145     Converter *Convert= preModel->Convert;
146     Converter *NextConvert= 0;
147 
148     while (model) {
149       while (model->model && !model->Convert) model= model->model;
150 
151       srcM= dstM;
152       if ((NextConvert= model->Convert)) {
153         /* There are two temporary arrays; NewTmpArray automatically
154            frees the array it allocated two calls before.  Both
155            arrays are deallocated by ClearTmpArray, which is also called
156            from YError.  */
157         Dimension *dims= NewDimension(number, 1L, (Dimension *)0);
158         Array *array= NewTmpArray(model, dims);
159         dims->references--;
160         dstM= array->value.c;
161       } else {
162         /* last pass always takes this branch */
163         dstM= dst;
164       }
165 
166       Convert(preModel, srcM, dstM, number, 0);
167       preModel= model;
168       model= model->model;
169       Convert= NextConvert;
170     }
171 
172     ClearTmpArray();
173   }
174 
175   if (doPointees && base->file) ReadPointees(base->file);
176 }
177 
178 /* In a write/scatter operation, base describes the data type of the
179    destination (which is either in memory or on disk).  The source is
180    assumed to have been allocated prior to this call, and to have the
181    data type base->model->...->model.
182    If base->file, then dstM==0 will cause a subsequent call to
183    WritePointees, while if dstM!=0, the call will not be made
184    (this is intended to prevent recursion).  */
WriteScatter(void * src,void * dstM,long dstD,StructDef * base,long number,const Strider * strider)185 void WriteScatter(void *src, void *dstM, long dstD, StructDef *base,
186                   long number, const Strider *strider)
187 {
188   StructDef *preModel= NeedsConversion(base);
189   void *sbuffer;
190 
191   if (preModel) {
192     ClearTmpArray();
193     /* Note that SetSequentialWrite has set seqAddress properly BEFORE
194        the call to WriteScatter -- DO NOT do anything like this:
195        if (base->file && base->addressType==2) base->file->seqAddress= dstD;
196      */
197     sbuffer= WriteRecurse(src, preModel, number);
198 
199   } else {
200     sbuffer= src;
201   }
202 
203   if (base->file) {
204     IOStream *file= base->file;
205     long nextAddress= file->nextAddress;
206     CastWrite(sbuffer, dstD, base, number, strider);
207     if (!dstM) {
208       WritePointees(file);
209       if (nextAddress<file->nextAddress) FlushFile(file, 0);
210     }
211   } else {
212     Scatter(sbuffer, dstM, base, number, strider);
213   }
214 
215   if (preModel) ClearTmpArray();
216 }
217 
WriteRecurse(void * src,StructDef * base,long number)218 static void *WriteRecurse(void *src, StructDef *base, long number)
219 {
220   StructDef *model= base->model;
221   Converter *Convert= base->Convert;  /* guaranteed non-zero */
222   Array *darray;
223   void *dst;
224   Dimension *dims;
225 
226   while (model->model && !model->Convert) model= model->model;
227 
228   if (model->Convert) src= WriteRecurse(src, model, number);
229 
230   /* There are two temporary arrays; NewTmpArray automatically
231      frees the array it allocated two calls before.  Both
232      arrays are deallocated by ClearTmpArray, which is also called
233      from YError.  */
234   dims= NewDimension(number, 1L, (Dimension *)0);
235   darray= NewTmpArray(base, dims);
236   dims->references--;
237   dst= darray->value.c;
238 
239   Convert(base, dst, src, number, 1);
240   return dst;
241 }
242 
243 /*--------------------------------------------------------------------------*/
244 
ReadPointees(IOStream * file)245 void ReadPointees(IOStream *file)
246 {
247   long nValid= file->pointeeList.nValid;
248   long n= file->pointeeList.table.nItems - nValid;
249   MDinfo *mdInfo= &file->pointeeList.mdInfo[nValid];
250   Array *array;
251   int noRecursion= 1;
252 
253   if (file->pointeeList.writing!=0) return;
254 
255   /* mark that read is in progress */
256   file->pointeeList.writing= 2;
257 
258   /* The Convert routine has already read the header, storing
259      the addresses of the header, the data, and the memory address
260      of a newly created Array into pointeeList.mdInfo.  */
261 
262   while (n>0) {
263     if (mdInfo->m) {
264       array= Pointee(mdInfo->m);
265       ReadGather(array->value.c, &noRecursion, mdInfo->data,
266                  mdInfo->base, array->type.number, (Strider *)0);
267     }
268 
269     /* note that pointeeList.table.nItems may have increased
270        during the reading of this pointee */
271     file->pointeeList.nValid= (++nValid);
272     mdInfo= &file->pointeeList.mdInfo[nValid];
273     n= file->pointeeList.table.nItems - nValid;
274   }
275 
276   /* successful completion of read */
277   file->pointeeList.writing= 0;
278 }
279 
WritePointees(IOStream * file)280 void WritePointees(IOStream *file)
281 {
282   long nValid= file->pointeeList.nValid;
283   long n= file->pointeeList.table.nItems - nValid;
284   MDinfo *mdInfo= &file->pointeeList.mdInfo[nValid];
285   Array *array;
286   int noRecursion= 1;
287 
288   if (file->pointeeList.writing!=1) return;
289 
290   /* mark that write is in progress */
291   file->pointeeList.writing= 3;
292 
293   /* The Convert routine has already written the header, storing
294      the addresses of the header, the data, and the memory data
295      into pointeeList.mdInfo.  */
296 
297   while (n>0) {
298     if (mdInfo->m) {
299       array= Pointee(mdInfo->m);
300       WriteScatter(array->value.c, &noRecursion, mdInfo->data,
301                    mdInfo->base, array->type.number, (Strider *)0);
302     }
303 
304     /* note that pointeeList.table.nItems may have increased
305        during the writing of this pointee */
306     file->pointeeList.nValid= (++nValid);
307     mdInfo= &file->pointeeList.mdInfo[nValid];
308     n= file->pointeeList.table.nItems - nValid;
309   }
310 
311   /* successful completion of write */
312   file->pointeeList.writing= 1;
313 }
314 
ClearPointees(IOStream * file,int writing)315 void ClearPointees(IOStream *file, int writing)
316 {
317   long i, nItems= file->pointeeList.table.nItems;
318   MDinfo *mdInfo;
319   Array *array;
320 
321   if (nItems && !(file->pointeeList.writing&2)) {
322     /* Note that this is skipped if failed at previous attempt to
323        call WritePointees or ReadPointees.  */
324     if (file->pointeeList.writing) WritePointees(file);
325     else ReadPointees(file);
326     /* tracking down pointers may have changed nItems, mdInfo */
327     nItems= file->pointeeList.table.nItems;
328   }
329 
330   mdInfo= file->pointeeList.mdInfo;
331   HashXClear(&file->pointeeList.table);
332   file->pointeeList.nValid= 0;
333   file->pointeeList.mdInfo= 0;
334   file->pointeeList.writing= writing;
335 
336   for (i=0 ; i<nItems ; i++) {
337     array= Pointee(mdInfo[i].m);
338     Unref(array);
339   }
340   p_free(mdInfo);
341 }
342 
343 /*--------------------------------------------------------------------------*/
344