xref: /petsc/src/sys/fileio/sysio.c (revision a69119a591a03a9d906b29c0a4e9802e4d7c9795)
1 
2 /*
3    This file contains simple binary read/write routines.
4  */
5 
6 #include <petscsys.h>
7 #include <petscbt.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #if defined(PETSC_HAVE_UNISTD_H)
11 #include <unistd.h>
12 #endif
13 #if defined(PETSC_HAVE_IO_H)
14 #include <io.h>
15 #endif
16 #if !defined(PETSC_HAVE_O_BINARY)
17 #define O_BINARY 0
18 #endif
19 
20 const char *const PetscFileModes[] = {"READ", "WRITE", "APPEND", "UPDATE", "APPEND_UPDATE", "PetscFileMode", "PETSC_FILE_", NULL};
21 
22 /* --------------------------------------------------------- */
23 /*
24   PetscByteSwapEnum - Swap bytes in a  PETSc Enum
25 
26 */
27 PetscErrorCode PetscByteSwapEnum(PetscEnum *buff, PetscInt n) {
28   PetscInt  i, j;
29   PetscEnum tmp = ENUM_DUMMY;
30   char     *ptr1, *ptr2 = (char *)&tmp;
31 
32   PetscFunctionBegin;
33   for (j = 0; j < n; j++) {
34     ptr1 = (char *)(buff + j);
35     for (i = 0; i < (PetscInt)sizeof(PetscEnum); i++) ptr2[i] = ptr1[sizeof(PetscEnum) - 1 - i];
36     for (i = 0; i < (PetscInt)sizeof(PetscEnum); i++) ptr1[i] = ptr2[i];
37   }
38   PetscFunctionReturn(0);
39 }
40 
41 /*
42   PetscByteSwapBool - Swap bytes in a  PETSc Bool
43 
44 */
45 PetscErrorCode PetscByteSwapBool(PetscBool *buff, PetscInt n) {
46   PetscInt  i, j;
47   PetscBool tmp = PETSC_FALSE;
48   char     *ptr1, *ptr2 = (char *)&tmp;
49 
50   PetscFunctionBegin;
51   for (j = 0; j < n; j++) {
52     ptr1 = (char *)(buff + j);
53     for (i = 0; i < (PetscInt)sizeof(PetscBool); i++) ptr2[i] = ptr1[sizeof(PetscBool) - 1 - i];
54     for (i = 0; i < (PetscInt)sizeof(PetscBool); i++) ptr1[i] = ptr2[i];
55   }
56   PetscFunctionReturn(0);
57 }
58 
59 /*
60   PetscByteSwapInt - Swap bytes in a  PETSc integer (which may be 32 or 64 bits)
61 
62 */
63 PetscErrorCode PetscByteSwapInt(PetscInt *buff, PetscInt n) {
64   PetscInt i, j, tmp = 0;
65   char    *ptr1, *ptr2 = (char *)&tmp;
66 
67   PetscFunctionBegin;
68   for (j = 0; j < n; j++) {
69     ptr1 = (char *)(buff + j);
70     for (i = 0; i < (PetscInt)sizeof(PetscInt); i++) ptr2[i] = ptr1[sizeof(PetscInt) - 1 - i];
71     for (i = 0; i < (PetscInt)sizeof(PetscInt); i++) ptr1[i] = ptr2[i];
72   }
73   PetscFunctionReturn(0);
74 }
75 
76 /*
77   PetscByteSwapInt64 - Swap bytes in a  PETSc integer (64 bits)
78 
79 */
80 PetscErrorCode PetscByteSwapInt64(PetscInt64 *buff, PetscInt n) {
81   PetscInt   i, j;
82   PetscInt64 tmp = 0;
83   char      *ptr1, *ptr2 = (char *)&tmp;
84 
85   PetscFunctionBegin;
86   for (j = 0; j < n; j++) {
87     ptr1 = (char *)(buff + j);
88     for (i = 0; i < (PetscInt)sizeof(PetscInt64); i++) ptr2[i] = ptr1[sizeof(PetscInt64) - 1 - i];
89     for (i = 0; i < (PetscInt)sizeof(PetscInt64); i++) ptr1[i] = ptr2[i];
90   }
91   PetscFunctionReturn(0);
92 }
93 
94 /* --------------------------------------------------------- */
95 /*
96   PetscByteSwapShort - Swap bytes in a short
97 */
98 PetscErrorCode PetscByteSwapShort(short *buff, PetscInt n) {
99   PetscInt i, j;
100   short    tmp;
101   char    *ptr1, *ptr2 = (char *)&tmp;
102 
103   PetscFunctionBegin;
104   for (j = 0; j < n; j++) {
105     ptr1 = (char *)(buff + j);
106     for (i = 0; i < (PetscInt)sizeof(short); i++) ptr2[i] = ptr1[sizeof(short) - 1 - i];
107     for (i = 0; i < (PetscInt)sizeof(short); i++) ptr1[i] = ptr2[i];
108   }
109   PetscFunctionReturn(0);
110 }
111 /*
112   PetscByteSwapLong - Swap bytes in a long
113 */
114 PetscErrorCode PetscByteSwapLong(long *buff, PetscInt n) {
115   PetscInt i, j;
116   long     tmp;
117   char    *ptr1, *ptr2 = (char *)&tmp;
118 
119   PetscFunctionBegin;
120   for (j = 0; j < n; j++) {
121     ptr1 = (char *)(buff + j);
122     for (i = 0; i < (PetscInt)sizeof(long); i++) ptr2[i] = ptr1[sizeof(long) - 1 - i];
123     for (i = 0; i < (PetscInt)sizeof(long); i++) ptr1[i] = ptr2[i];
124   }
125   PetscFunctionReturn(0);
126 }
127 /* --------------------------------------------------------- */
128 /*
129   PetscByteSwapReal - Swap bytes in a PetscReal
130 */
131 PetscErrorCode PetscByteSwapReal(PetscReal *buff, PetscInt n) {
132   PetscInt  i, j;
133   PetscReal tmp, *buff1 = (PetscReal *)buff;
134   char     *ptr1, *ptr2 = (char *)&tmp;
135 
136   PetscFunctionBegin;
137   for (j = 0; j < n; j++) {
138     ptr1 = (char *)(buff1 + j);
139     for (i = 0; i < (PetscInt)sizeof(PetscReal); i++) ptr2[i] = ptr1[sizeof(PetscReal) - 1 - i];
140     for (i = 0; i < (PetscInt)sizeof(PetscReal); i++) ptr1[i] = ptr2[i];
141   }
142   PetscFunctionReturn(0);
143 }
144 /* --------------------------------------------------------- */
145 /*
146   PetscByteSwapScalar - Swap bytes in a PetscScalar
147   The complex case is dealt with with an array of PetscReal, twice as long.
148 */
149 PetscErrorCode PetscByteSwapScalar(PetscScalar *buff, PetscInt n) {
150   PetscInt  i, j;
151   PetscReal tmp, *buff1 = (PetscReal *)buff;
152   char     *ptr1, *ptr2 = (char *)&tmp;
153 
154   PetscFunctionBegin;
155 #if defined(PETSC_USE_COMPLEX)
156   n *= 2;
157 #endif
158   for (j = 0; j < n; j++) {
159     ptr1 = (char *)(buff1 + j);
160     for (i = 0; i < (PetscInt)sizeof(PetscReal); i++) ptr2[i] = ptr1[sizeof(PetscReal) - 1 - i];
161     for (i = 0; i < (PetscInt)sizeof(PetscReal); i++) ptr1[i] = ptr2[i];
162   }
163   PetscFunctionReturn(0);
164 }
165 /* --------------------------------------------------------- */
166 /*
167   PetscByteSwapDouble - Swap bytes in a double
168 */
169 PetscErrorCode PetscByteSwapDouble(double *buff, PetscInt n) {
170   PetscInt i, j;
171   double   tmp, *buff1 = (double *)buff;
172   char    *ptr1, *ptr2 = (char *)&tmp;
173 
174   PetscFunctionBegin;
175   for (j = 0; j < n; j++) {
176     ptr1 = (char *)(buff1 + j);
177     for (i = 0; i < (PetscInt)sizeof(double); i++) ptr2[i] = ptr1[sizeof(double) - 1 - i];
178     for (i = 0; i < (PetscInt)sizeof(double); i++) ptr1[i] = ptr2[i];
179   }
180   PetscFunctionReturn(0);
181 }
182 
183 /*
184   PetscByteSwapFloat - Swap bytes in a float
185 */
186 PetscErrorCode PetscByteSwapFloat(float *buff, PetscInt n) {
187   PetscInt i, j;
188   float    tmp, *buff1 = (float *)buff;
189   char    *ptr1, *ptr2 = (char *)&tmp;
190 
191   PetscFunctionBegin;
192   for (j = 0; j < n; j++) {
193     ptr1 = (char *)(buff1 + j);
194     for (i = 0; i < (PetscInt)sizeof(float); i++) ptr2[i] = ptr1[sizeof(float) - 1 - i];
195     for (i = 0; i < (PetscInt)sizeof(float); i++) ptr1[i] = ptr2[i];
196   }
197   PetscFunctionReturn(0);
198 }
199 
200 PetscErrorCode PetscByteSwap(void *data, PetscDataType pdtype, PetscInt count) {
201   PetscFunctionBegin;
202   if (pdtype == PETSC_INT) PetscCall(PetscByteSwapInt((PetscInt *)data, count));
203   else if (pdtype == PETSC_ENUM) PetscCall(PetscByteSwapEnum((PetscEnum *)data, count));
204   else if (pdtype == PETSC_BOOL) PetscCall(PetscByteSwapBool((PetscBool *)data, count));
205   else if (pdtype == PETSC_SCALAR) PetscCall(PetscByteSwapScalar((PetscScalar *)data, count));
206   else if (pdtype == PETSC_REAL) PetscCall(PetscByteSwapReal((PetscReal *)data, count));
207   else if (pdtype == PETSC_COMPLEX) PetscCall(PetscByteSwapReal((PetscReal *)data, 2 * count));
208   else if (pdtype == PETSC_INT64) PetscCall(PetscByteSwapInt64((PetscInt64 *)data, count));
209   else if (pdtype == PETSC_DOUBLE) PetscCall(PetscByteSwapDouble((double *)data, count));
210   else if (pdtype == PETSC_FLOAT) PetscCall(PetscByteSwapFloat((float *)data, count));
211   else if (pdtype == PETSC_SHORT) PetscCall(PetscByteSwapShort((short *)data, count));
212   else if (pdtype == PETSC_LONG) PetscCall(PetscByteSwapLong((long *)data, count));
213   PetscFunctionReturn(0);
214 }
215 
216 /*@C
217    PetscBinaryRead - Reads from a binary file.
218 
219    Not Collective
220 
221    Input Parameters:
222 +  fd - the file descriptor
223 .  num  - the maximum number of items to read
224 -  type - the type of items to read (PETSC_INT, PETSC_REAL, PETSC_SCALAR, etc.)
225 
226    Output Parameters:
227 +  data - the buffer
228 -  count - the number of items read, optional
229 
230    Level: developer
231 
232    Notes:
233    If count is not provided and the number of items read is less than
234    the maximum number of items to read, then this routine errors.
235 
236    PetscBinaryRead() uses byte swapping to work on all machines; the files
237    are written to file ALWAYS using big-endian ordering. On little-endian machines the numbers
238    are converted to the little-endian format when they are read in from the file.
239    When PETSc is ./configure with --with-64-bit-indices the integers are written to the
240    file as 64 bit integers, this means they can only be read back in when the option --with-64-bit-indices
241    is used.
242 
243 .seealso: `PetscBinaryWrite()`, `PetscBinaryOpen()`, `PetscBinaryClose()`, `PetscViewerBinaryGetDescriptor()`, `PetscBinarySynchronizedWrite()`,
244           `PetscBinarySynchronizedRead()`, `PetscBinarySynchronizedSeek()`
245 @*/
246 PetscErrorCode PetscBinaryRead(int fd, void *data, PetscInt num, PetscInt *count, PetscDataType type) {
247   size_t typesize, m = (size_t)num, n = 0, maxblock = 65536;
248   char  *p = (char *)data;
249 #if defined(PETSC_USE_REAL___FLOAT128)
250   PetscBool readdouble = PETSC_FALSE;
251   double   *pdouble;
252 #endif
253   void *ptmp  = data;
254   char *fname = NULL;
255 
256   PetscFunctionBegin;
257   if (count) *count = 0;
258   PetscCheck(num >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Trying to read a negative amount of data %" PetscInt_FMT, num);
259   if (!num) PetscFunctionReturn(0);
260 
261   if (type == PETSC_FUNCTION) {
262     m     = 64;
263     type  = PETSC_CHAR;
264     fname = (char *)malloc(m * sizeof(char));
265     p     = (char *)fname;
266     ptmp  = (void *)fname;
267     PetscCheck(fname, PETSC_COMM_SELF, PETSC_ERR_MEM, "Cannot allocate space for function name");
268   }
269   if (type == PETSC_BIT_LOGICAL) m = PetscBTLength(m);
270 
271   PetscCall(PetscDataTypeGetSize(type, &typesize));
272 
273 #if defined(PETSC_USE_REAL___FLOAT128)
274   PetscCall(PetscOptionsGetBool(NULL, NULL, "-binary_read_double", &readdouble, NULL));
275   /* If using __float128 precision we still read in doubles from file */
276   if ((type == PETSC_REAL || type == PETSC_COMPLEX) && readdouble) {
277     PetscInt cnt = num * ((type == PETSC_REAL) ? 1 : 2);
278     PetscCall(PetscMalloc1(cnt, &pdouble));
279     p = (char *)pdouble;
280     typesize /= 2;
281   }
282 #endif
283 
284   m *= typesize;
285 
286   while (m) {
287     size_t len = (m < maxblock) ? m : maxblock;
288     int    ret = (int)read(fd, p, len);
289     if (ret < 0 && errno == EINTR) continue;
290     if (!ret && len > 0) break; /* Proxy for EOF */
291     PetscCheck(ret >= 0, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from file, errno %d", errno);
292     m -= (size_t)ret;
293     p += ret;
294     n += (size_t)ret;
295   }
296   PetscCheck(!m || count, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Read past end of file");
297 
298   num = (PetscInt)(n / typesize); /* Should we require `n % typesize == 0` ? */
299   if (count) *count = num;        /* TODO: This is most likely wrong for PETSC_BIT_LOGICAL */
300 
301 #if defined(PETSC_USE_REAL___FLOAT128)
302   if ((type == PETSC_REAL || type == PETSC_COMPLEX) && readdouble) {
303     PetscInt   i, cnt = num * ((type == PETSC_REAL) ? 1 : 2);
304     PetscReal *preal = (PetscReal *)data;
305     if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwapDouble(pdouble, cnt));
306     for (i = 0; i < cnt; i++) preal[i] = pdouble[i];
307     PetscCall(PetscFree(pdouble));
308     PetscFunctionReturn(0);
309   }
310 #endif
311 
312   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap(ptmp, type, num));
313 
314   if (type == PETSC_FUNCTION) {
315 #if defined(PETSC_SERIALIZE_FUNCTIONS)
316     PetscCall(PetscDLSym(NULL, fname, (void **)data));
317 #else
318     *(void **)data = NULL;
319 #endif
320     free(fname);
321   }
322   PetscFunctionReturn(0);
323 }
324 
325 /*@C
326    PetscBinaryWrite - Writes to a binary file.
327 
328    Not Collective
329 
330    Input Parameters:
331 +  fd     - the file
332 .  p      - the buffer
333 .  n      - the number of items to write
334 -  type   - the type of items to read (PETSC_INT, PETSC_DOUBLE or PETSC_SCALAR)
335 
336    Level: advanced
337 
338    Notes:
339    PetscBinaryWrite() uses byte swapping to work on all machines; the files
340    are written using big-endian ordering to the file. On little-endian machines the numbers
341    are converted to the big-endian format when they are written to disk.
342    When PETSc is ./configure with --with-64-bit-indices the integers are written to the
343    file as 64 bit integers, this means they can only be read back in when the option --with-64-bit-indices
344    is used.
345 
346    If running with __float128 precision the output is in __float128 unless one uses the -binary_write_double option
347 
348    The Buffer p should be read-write buffer, and not static data.
349    This way, byte-swapping is done in-place, and then the buffer is
350    written to the file.
351 
352    This routine restores the original contents of the buffer, after
353    it is written to the file. This is done by byte-swapping in-place
354    the second time.
355 
356    Because byte-swapping may be done on the values in data it cannot be declared const
357 
358 .seealso: `PetscBinaryRead()`, `PetscBinaryOpen()`, `PetscBinaryClose()`, `PetscViewerBinaryGetDescriptor()`, `PetscBinarySynchronizedWrite()`,
359           `PetscBinarySynchronizedRead()`, `PetscBinarySynchronizedSeek()`
360 @*/
361 PetscErrorCode PetscBinaryWrite(int fd, const void *p, PetscInt n, PetscDataType type) {
362   const char *pp = (char *)p;
363   int         err, wsize;
364   size_t      m = (size_t)n, maxblock = 65536;
365   const void *ptmp  = p;
366   char       *fname = NULL;
367 #if defined(PETSC_USE_REAL___FLOAT128)
368   PetscBool  writedouble = PETSC_FALSE;
369   double    *ppp;
370   PetscReal *pv;
371   PetscInt   i;
372 #endif
373   PetscDataType wtype = type;
374 
375   PetscFunctionBegin;
376   PetscCheck(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Trying to write a negative amount of data %" PetscInt_FMT, n);
377   if (!n) PetscFunctionReturn(0);
378 
379   if (type == PETSC_FUNCTION) {
380 #if defined(PETSC_SERIALIZE_FUNCTIONS)
381     const char *fnametmp;
382 #endif
383     m     = 64;
384     fname = (char *)malloc(m * sizeof(char));
385     PetscCheck(fname, PETSC_COMM_SELF, PETSC_ERR_MEM, "Cannot allocate space for function name");
386 #if defined(PETSC_SERIALIZE_FUNCTIONS)
387     PetscCheck(n <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Can only binary view a single function at a time");
388     PetscCall(PetscFPTFind(*(void **)p, &fnametmp));
389     PetscCall(PetscStrncpy(fname, fnametmp, m));
390 #else
391     PetscCall(PetscStrncpy(fname, "", m));
392 #endif
393     wtype = PETSC_CHAR;
394     pp    = (char *)fname;
395     ptmp  = (void *)fname;
396   }
397 
398 #if defined(PETSC_USE_REAL___FLOAT128)
399   PetscCall(PetscOptionsGetBool(NULL, NULL, "-binary_write_double", &writedouble, NULL));
400   /* If using __float128 precision we still write in doubles to file */
401   if ((type == PETSC_SCALAR || type == PETSC_REAL || type == PETSC_COMPLEX) && writedouble) {
402     wtype = PETSC_DOUBLE;
403     PetscCall(PetscMalloc1(n, &ppp));
404     pv = (PetscReal *)pp;
405     for (i = 0; i < n; i++) ppp[i] = (double)pv[i];
406     pp   = (char *)ppp;
407     ptmp = (char *)ppp;
408   }
409 #endif
410 
411   if (wtype == PETSC_INT) m *= sizeof(PetscInt);
412   else if (wtype == PETSC_SCALAR) m *= sizeof(PetscScalar);
413 #if defined(PETSC_HAVE_COMPLEX)
414   else if (wtype == PETSC_COMPLEX) m *= sizeof(PetscComplex);
415 #endif
416   else if (wtype == PETSC_REAL) m *= sizeof(PetscReal);
417   else if (wtype == PETSC_DOUBLE) m *= sizeof(double);
418   else if (wtype == PETSC_FLOAT) m *= sizeof(float);
419   else if (wtype == PETSC_SHORT) m *= sizeof(short);
420   else if (wtype == PETSC_LONG) m *= sizeof(long);
421   else if (wtype == PETSC_CHAR) m *= sizeof(char);
422   else if (wtype == PETSC_ENUM) m *= sizeof(PetscEnum);
423   else if (wtype == PETSC_BOOL) m *= sizeof(PetscBool);
424   else if (wtype == PETSC_INT64) m *= sizeof(PetscInt64);
425   else if (wtype == PETSC_BIT_LOGICAL) m = PetscBTLength(m) * sizeof(char);
426   else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Unknown type");
427 
428   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap((void *)ptmp, wtype, n));
429 
430   while (m) {
431     wsize = (m < maxblock) ? m : maxblock;
432     err   = write(fd, pp, wsize);
433     if (err < 0 && errno == EINTR) continue;
434     PetscCheck(err == wsize, PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE, "Error writing to file total size %d err %d wsize %d", (int)n, (int)err, (int)wsize);
435     m -= wsize;
436     pp += wsize;
437   }
438 
439   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap((void *)ptmp, wtype, n));
440 
441   if (type == PETSC_FUNCTION) free(fname);
442 #if defined(PETSC_USE_REAL___FLOAT128)
443   if ((type == PETSC_SCALAR || type == PETSC_REAL || type == PETSC_COMPLEX) && writedouble) PetscCall(PetscFree(ppp));
444 #endif
445   PetscFunctionReturn(0);
446 }
447 
448 /*@C
449    PetscBinaryOpen - Opens a PETSc binary file.
450 
451    Not Collective
452 
453    Input Parameters:
454 +  name - filename
455 -  mode - open mode of binary file, one of FILE_MODE_READ, FILE_MODE_WRITE, FILE_MODE_APPEND
456 
457    Output Parameter:
458 .  fd - the file
459 
460    Level: advanced
461 
462    Notes:
463     Files access with PetscBinaryRead() and PetscBinaryWrite() are ALWAYS written in
464    big-endian format. This means the file can be accessed using PetscBinaryOpen() and
465    PetscBinaryRead() and PetscBinaryWrite() on any machine.
466 
467 .seealso: `PetscBinaryRead()`, `PetscBinaryWrite()`, `PetscFileMode`, `PetscViewerFileSetMode()`, `PetscViewerBinaryGetDescriptor()`,
468           `PetscBinarySynchronizedWrite()`, `PetscBinarySynchronizedRead()`, `PetscBinarySynchronizedSeek()`
469 
470 @*/
471 PetscErrorCode PetscBinaryOpen(const char name[], PetscFileMode mode, int *fd) {
472   PetscFunctionBegin;
473   switch (mode) {
474   case FILE_MODE_READ: *fd = open(name, O_BINARY | O_RDONLY, 0); break;
475   case FILE_MODE_WRITE: *fd = open(name, O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, 0666); break;
476   case FILE_MODE_APPEND: *fd = open(name, O_BINARY | O_WRONLY | O_APPEND, 0); break;
477   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unsupported file mode %s", PetscFileModes[mode]);
478   }
479   PetscCheck(*fd != -1, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot open file %s for %s", name, PetscFileModes[mode]);
480   PetscFunctionReturn(0);
481 }
482 
483 /*@
484    PetscBinaryClose - Closes a PETSc binary file.
485 
486    Not Collective
487 
488    Output Parameter:
489 .  fd - the file
490 
491    Level: advanced
492 
493 .seealso: `PetscBinaryRead()`, `PetscBinaryWrite()`, `PetscBinaryOpen()`, `PetscBinarySynchronizedWrite()`, `PetscBinarySynchronizedRead()`,
494           `PetscBinarySynchronizedSeek()`
495 @*/
496 PetscErrorCode PetscBinaryClose(int fd) {
497   PetscFunctionBegin;
498   PetscCheck(!close(fd), PETSC_COMM_SELF, PETSC_ERR_SYS, "close() failed on file descriptor");
499   PetscFunctionReturn(0);
500 }
501 
502 /*@C
503    PetscBinarySeek - Moves the file pointer on a PETSc binary file.
504 
505    Not Collective
506 
507    Input Parameters:
508 +  fd - the file
509 .  off - number of bytes to move. Use PETSC_BINARY_INT_SIZE, PETSC_BINARY_SCALAR_SIZE,
510             etc. in your calculation rather than sizeof() to compute byte lengths.
511 -  whence - if PETSC_BINARY_SEEK_SET then off is an absolute location in the file
512             if PETSC_BINARY_SEEK_CUR then off is an offset from the current location
513             if PETSC_BINARY_SEEK_END then off is an offset from the end of file
514 
515    Output Parameter:
516 .   offset - new offset in file
517 
518    Level: developer
519 
520    Notes:
521    Integers are stored on the file as 32 long, regardless of whether
522    they are stored in the machine as 32 or 64, this means the same
523    binary file may be read on any machine. Hence you CANNOT use sizeof()
524    to determine the offset or location.
525 
526 .seealso: `PetscBinaryRead()`, `PetscBinaryWrite()`, `PetscBinaryOpen()`, `PetscBinarySynchronizedWrite()`, `PetscBinarySynchronizedRead()`,
527           `PetscBinarySynchronizedSeek()`
528 @*/
529 PetscErrorCode PetscBinarySeek(int fd, off_t off, PetscBinarySeekType whence, off_t *offset) {
530   int iwhence = 0;
531 
532   PetscFunctionBegin;
533   if (whence == PETSC_BINARY_SEEK_SET) iwhence = SEEK_SET;
534   else if (whence == PETSC_BINARY_SEEK_CUR) iwhence = SEEK_CUR;
535   else if (whence == PETSC_BINARY_SEEK_END) iwhence = SEEK_END;
536   else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Unknown seek location");
537 #if defined(PETSC_HAVE_LSEEK)
538   *offset = lseek(fd, off, iwhence);
539 #elif defined(PETSC_HAVE__LSEEK)
540   *offset = _lseek(fd, (long)off, iwhence);
541 #else
542   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "System does not have a way of seeking on a file");
543 #endif
544   PetscFunctionReturn(0);
545 }
546 
547 /*@C
548    PetscBinarySynchronizedRead - Reads from a binary file.
549 
550    Collective
551 
552    Input Parameters:
553 +  comm - the MPI communicator
554 .  fd - the file descriptor
555 .  num  - the maximum number of items to read
556 -  type - the type of items to read (PETSC_INT, PETSC_REAL, PETSC_SCALAR, etc.)
557 
558    Output Parameters:
559 +  data - the buffer
560 -  count - the number of items read, optional
561 
562    Level: developer
563 
564    Notes:
565    Does a PetscBinaryRead() followed by an MPI_Bcast()
566 
567    If count is not provided and the number of items read is less than
568    the maximum number of items to read, then this routine errors.
569 
570    PetscBinarySynchronizedRead() uses byte swapping to work on all machines.
571    Integers are stored on the file as 32 long, regardless of whether
572    they are stored in the machine as 32 or 64, this means the same
573    binary file may be read on any machine.
574 
575 .seealso: `PetscBinaryWrite()`, `PetscBinaryOpen()`, `PetscBinaryClose()`, `PetscBinaryRead()`, `PetscBinarySynchronizedWrite()`,
576           `PetscBinarySynchronizedSeek()`
577 @*/
578 PetscErrorCode PetscBinarySynchronizedRead(MPI_Comm comm, int fd, void *data, PetscInt num, PetscInt *count, PetscDataType type) {
579   PetscMPIInt  rank, size;
580   MPI_Datatype mtype;
581   PetscInt     ibuf[2] = {0, 0};
582   char        *fname   = NULL;
583   void        *fptr    = NULL;
584 
585   PetscFunctionBegin;
586   if (type == PETSC_FUNCTION) {
587     num   = 64;
588     type  = PETSC_CHAR;
589     fname = (char *)malloc(num * sizeof(char));
590     fptr  = data;
591     data  = (void *)fname;
592     PetscCheck(fname, PETSC_COMM_SELF, PETSC_ERR_MEM, "Cannot allocate space for function name");
593   }
594 
595   PetscCallMPI(MPI_Comm_rank(comm, &rank));
596   PetscCallMPI(MPI_Comm_size(comm, &size));
597   if (rank == 0) ibuf[0] = PetscBinaryRead(fd, data, num, count ? &ibuf[1] : NULL, type);
598   PetscCallMPI(MPI_Bcast(ibuf, 2, MPIU_INT, 0, comm));
599   PetscCall((PetscErrorCode)ibuf[0]);
600 
601   /* skip MPI call on potentially huge amounts of data when running with one process; this allows the amount of data to basically unlimited in that case */
602   if (size > 1) {
603     PetscCall(PetscDataTypeToMPIDataType(type, &mtype));
604     PetscCallMPI(MPI_Bcast(data, count ? ibuf[1] : num, mtype, 0, comm));
605   }
606   if (count) *count = ibuf[1];
607 
608   if (type == PETSC_FUNCTION) {
609 #if defined(PETSC_SERIALIZE_FUNCTIONS)
610     PetscCall(PetscDLLibrarySym(PETSC_COMM_SELF, &PetscDLLibrariesLoaded, NULL, fname, (void **)fptr));
611 #else
612     *(void **)fptr = NULL;
613 #endif
614     free(fname);
615   }
616   PetscFunctionReturn(0);
617 }
618 
619 /*@C
620    PetscBinarySynchronizedWrite - writes to a binary file.
621 
622    Collective
623 
624    Input Parameters:
625 +  comm - the MPI communicator
626 .  fd - the file
627 .  n  - the number of items to write
628 .  p - the buffer
629 -  type - the type of items to write (PETSC_INT, PETSC_DOUBLE or PETSC_SCALAR)
630 
631    Level: developer
632 
633    Notes:
634    Process 0 does a PetscBinaryWrite()
635 
636    PetscBinarySynchronizedWrite() uses byte swapping to work on all machines.
637    Integers are stored on the file as 32 long, regardless of whether
638    they are stored in the machine as 32 or 64, this means the same
639    binary file may be read on any machine.
640 
641    Notes:
642     because byte-swapping may be done on the values in data it cannot be declared const
643 
644    WARNING: This is NOT like PetscSynchronizedFPrintf()! This routine ignores calls on all but process 0,
645    while PetscSynchronizedFPrintf() has all processes print their strings in order.
646 
647 .seealso: `PetscBinaryWrite()`, `PetscBinaryOpen()`, `PetscBinaryClose()`, `PetscBinaryRead()`, `PetscBinarySynchronizedRead()`,
648           `PetscBinarySynchronizedSeek()`
649 @*/
650 PetscErrorCode PetscBinarySynchronizedWrite(MPI_Comm comm, int fd, const void *p, PetscInt n, PetscDataType type) {
651   PetscMPIInt rank;
652 
653   PetscFunctionBegin;
654   PetscCallMPI(MPI_Comm_rank(comm, &rank));
655   if (rank == 0) PetscCall(PetscBinaryWrite(fd, p, n, type));
656   PetscFunctionReturn(0);
657 }
658 
659 /*@C
660    PetscBinarySynchronizedSeek - Moves the file pointer on a PETSc binary file.
661 
662    Input Parameters:
663 +  fd - the file
664 .  whence - if PETSC_BINARY_SEEK_SET then size is an absolute location in the file
665             if PETSC_BINARY_SEEK_CUR then size is offset from current location
666             if PETSC_BINARY_SEEK_END then size is offset from end of file
667 -  off    - number of bytes to move. Use PETSC_BINARY_INT_SIZE, PETSC_BINARY_SCALAR_SIZE,
668             etc. in your calculation rather than sizeof() to compute byte lengths.
669 
670    Output Parameter:
671 .   offset - new offset in file
672 
673    Level: developer
674 
675    Notes:
676    Integers are stored on the file as 32 long, regardless of whether
677    they are stored in the machine as 32 or 64, this means the same
678    binary file may be read on any machine. Hence you CANNOT use sizeof()
679    to determine the offset or location.
680 
681 .seealso: `PetscBinaryRead()`, `PetscBinaryWrite()`, `PetscBinaryOpen()`, `PetscBinarySynchronizedWrite()`, `PetscBinarySynchronizedRead()`,
682           `PetscBinarySynchronizedSeek()`
683 @*/
684 PetscErrorCode PetscBinarySynchronizedSeek(MPI_Comm comm, int fd, off_t off, PetscBinarySeekType whence, off_t *offset) {
685   PetscMPIInt rank;
686 
687   PetscFunctionBegin;
688   PetscCallMPI(MPI_Comm_rank(comm, &rank));
689   if (rank == 0) PetscCall(PetscBinarySeek(fd, off, whence, offset));
690   PetscFunctionReturn(0);
691 }
692 
693 #if defined(PETSC_HAVE_MPIIO)
694 
695 #if defined(PETSC_USE_PETSC_MPI_EXTERNAL32)
696 /*
697       MPICH does not provide the external32 representation for MPI_File_set_view() so we need to provide the functions.
698     These are set into MPI in PetscInitialize() via MPI_Register_datarep()
699 
700     Note I use PetscMPIInt for the MPI error codes since that is what MPI uses (instead of the standard PetscErrorCode)
701 
702     The next three routines are not used because MPICH does not support their use
703 
704 */
705 PETSC_EXTERN PetscMPIInt PetscDataRep_extent_fn(MPI_Datatype datatype, MPI_Aint *file_extent, void *extra_state) {
706   MPI_Aint    ub;
707   PetscMPIInt ierr;
708 
709   ierr = MPI_Type_get_extent(datatype, &ub, file_extent);
710   return ierr;
711 }
712 
713 PETSC_EXTERN PetscMPIInt PetscDataRep_read_conv_fn(void *userbuf, MPI_Datatype datatype, PetscMPIInt count, void *filebuf, MPI_Offset position, void *extra_state) {
714   PetscDataType pdtype;
715   PetscMPIInt   ierr;
716   size_t        dsize;
717 
718   PetscCall(PetscMPIDataTypeToPetscDataType(datatype, &pdtype));
719   PetscCall(PetscDataTypeGetSize(pdtype, &dsize));
720 
721   /* offset is given in units of MPI_Datatype */
722   userbuf = ((char *)userbuf) + dsize * position;
723 
724   PetscCall(PetscMemcpy(userbuf, filebuf, count * dsize));
725   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap(userbuf, pdtype, count));
726   return ierr;
727 }
728 
729 PetscMPIInt PetscDataRep_write_conv_fn(void *userbuf, MPI_Datatype datatype, PetscMPIInt count, void *filebuf, MPI_Offset position, void *extra_state) {
730   PetscDataType pdtype;
731   PetscMPIInt   ierr;
732   size_t        dsize;
733 
734   PetscCall(PetscMPIDataTypeToPetscDataType(datatype, &pdtype));
735   PetscCall(PetscDataTypeGetSize(pdtype, &dsize));
736 
737   /* offset is given in units of MPI_Datatype */
738   userbuf = ((char *)userbuf) + dsize * position;
739 
740   PetscCall(PetscMemcpy(filebuf, userbuf, count * dsize));
741   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap(filebuf, pdtype, count));
742   return ierr;
743 }
744 #endif
745 
746 PetscErrorCode MPIU_File_write_all(MPI_File fd, void *data, PetscMPIInt cnt, MPI_Datatype dtype, MPI_Status *status) {
747   PetscDataType pdtype;
748 
749   PetscFunctionBegin;
750   PetscCall(PetscMPIDataTypeToPetscDataType(dtype, &pdtype));
751   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap(data, pdtype, cnt));
752   PetscCallMPI(MPI_File_write_all(fd, data, cnt, dtype, status));
753   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap(data, pdtype, cnt));
754   PetscFunctionReturn(0);
755 }
756 
757 PetscErrorCode MPIU_File_read_all(MPI_File fd, void *data, PetscMPIInt cnt, MPI_Datatype dtype, MPI_Status *status) {
758   PetscDataType pdtype;
759 
760   PetscFunctionBegin;
761   PetscCall(PetscMPIDataTypeToPetscDataType(dtype, &pdtype));
762   PetscCallMPI(MPI_File_read_all(fd, data, cnt, dtype, status));
763   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap(data, pdtype, cnt));
764   PetscFunctionReturn(0);
765 }
766 
767 PetscErrorCode MPIU_File_write_at(MPI_File fd, MPI_Offset off, void *data, PetscMPIInt cnt, MPI_Datatype dtype, MPI_Status *status) {
768   PetscDataType pdtype;
769 
770   PetscFunctionBegin;
771   PetscCall(PetscMPIDataTypeToPetscDataType(dtype, &pdtype));
772   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap(data, pdtype, cnt));
773   PetscCallMPI(MPI_File_write_at(fd, off, data, cnt, dtype, status));
774   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap(data, pdtype, cnt));
775   PetscFunctionReturn(0);
776 }
777 
778 PetscErrorCode MPIU_File_read_at(MPI_File fd, MPI_Offset off, void *data, PetscMPIInt cnt, MPI_Datatype dtype, MPI_Status *status) {
779   PetscDataType pdtype;
780 
781   PetscFunctionBegin;
782   PetscCall(PetscMPIDataTypeToPetscDataType(dtype, &pdtype));
783   PetscCallMPI(MPI_File_read_at(fd, off, data, cnt, dtype, status));
784   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap(data, pdtype, cnt));
785   PetscFunctionReturn(0);
786 }
787 
788 PetscErrorCode MPIU_File_write_at_all(MPI_File fd, MPI_Offset off, void *data, PetscMPIInt cnt, MPI_Datatype dtype, MPI_Status *status) {
789   PetscDataType pdtype;
790 
791   PetscFunctionBegin;
792   PetscCall(PetscMPIDataTypeToPetscDataType(dtype, &pdtype));
793   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap(data, pdtype, cnt));
794   PetscCallMPI(MPI_File_write_at_all(fd, off, data, cnt, dtype, status));
795   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap(data, pdtype, cnt));
796   PetscFunctionReturn(0);
797 }
798 
799 PetscErrorCode MPIU_File_read_at_all(MPI_File fd, MPI_Offset off, void *data, PetscMPIInt cnt, MPI_Datatype dtype, MPI_Status *status) {
800   PetscDataType pdtype;
801 
802   PetscFunctionBegin;
803   PetscCall(PetscMPIDataTypeToPetscDataType(dtype, &pdtype));
804   PetscCallMPI(MPI_File_read_at_all(fd, off, data, cnt, dtype, status));
805   if (!PetscBinaryBigEndian()) PetscCall(PetscByteSwap(data, pdtype, cnt));
806   PetscFunctionReturn(0);
807 }
808 
809 #endif
810