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