e3/ecl/fortio.h
Go to the documentation of this file.
1#ifndef ECL_FORTIO_H
2#define ECL_FORTIO_H
3
4#include <stdio.h>
5#include <stdint.h>
6
7#ifdef __cplusplus
8extern "C" {
9#endif
10
11/*
12 * As per the gnu fortran manual, int32 is sufficient for the record byte
13 * marker. optionally we could support 8-byte markers with either compile-time
14 * configuration or a run-time switch
15 *
16 * http://gcc.gnu.org/onlinedocs/gfortran/File-format-of-unformatted-sequential-files.html
17 *
18 * By default, all functions assume strict fortran compatibility (i.e. with
19 * trailing record size) and network (big-endian) byte order.
20 *
21 *
22 * A Fortran program writes unformatted data to file in a statemente like:
23 *
24 * integer array(100)
25 * write(unit) array
26 *
27 * it actually writes a head and tail in addition to the actual
28 * data. The header and tail is a 4 byte integer, which value is the
29 * number of bytes in the immediately following record. I.e. what is
30 * actually found on disk after the Fortran code above is:
31 *
32 * | 400 | array ...... | 400 |
33 *
34 */
35
36/*
37 * The ecl_fio functions are exception safe, that is, if a function fails, the
38 * file pointer is rewinded to before the function was called, and output
39 * parameters are not modified, as if the function was never called.
40 *
41 * This comes with a few exceptions:
42 * 1. if ECL_ERR_SEEK is returned, the roll-back of the file pointer itself
43 * failed and NOTHING IS GUARANTEED. The file stream is left in an unspecified
44 * state, and must be recovered accordingly.
45 * 2. in eclfio_get, the output record buffer must always be considered dirty
46 * and incomplete unless the function suceeds, or ECL_EINVAL is returned.
47 *
48 *
49 * ECL_ERR_SEEK should be rather rare, but to provide strong guarantees, this
50 * error must be handled carefully.
51 */
52
53/*
54 * every function takes a const char* opts parameter. This is a tiny
55 * configuration language inspired by printf and fopen. every character not in
56 * the set of keys is ignored. the opts parameter must be null terminated.
57 *
58 * if two options setting the same parameter (e.g. i and f, or e and E), the
59 * last one in the option string takes effect.
60 *
61 * options
62 * -------
63 * record data types:
64 * c - characters, sizeof(char)
65 * i - (signed)integers, sizeof(int32_t), default
66 * f - single-precision float, sizeof(float)
67 * d - double-precision float, sizeof(double)
68 *
69 * behaviour:
70 * E - assume big-endian record data (default)
71 * e - assume little-endian record data
72 * t - transform/byteswap data according to data type (default)
73 * T - don't transform/byteswap data (does not affect heads/tails)
74 *
75 * endianness parameter applies to both head, tail, and data, but head/tail can
76 * be interepreted with endianness byteswapping data by disabling transform
77 *
78 * fault tolerance:
79 * # - ignore size hint
80 * ~ - force no-tail (assume only head)
81 * $ - allow no-tail (don't fail on missing tail)
82 */
83
84/*
85 * Get the size (number of elements) of the current record. The file position
86 * is approperiately rewinded afterwards, as if the function was never called.
87 *
88 * If this function fails, out is not modified.
89 *
90 * If the read fails, ECL_ERR_READ is returned.
91 *
92 * This function is largely intended for peeking the size of the next record,
93 * to approperiately allocate a large enough buffer, which is useful when
94 * dealing with unknown files. If it is know in advance how large the records
95 * are, it is not necessary to call this function before reading a record.
96 */
97int eclfio_sizeof( FILE*, const char* opts, int32_t* out );
98
99/*
100 * Advance the file position n records. The file position is reset if the
101 * function fails, as if the function was never called.
102 *
103 * Returns ECL_OK if all records were skipped. If it fails, either
104 * ECL_INVALID_RECORD or ECL_ERR_READ is returned, depending on the source of
105 * the error, same rules as that of eclfio_get.
106 *
107 * This function does not distinguish seek errors for any n not +-1, so to
108 * figure out which record fails, one record at a time must be skipped.
109 */
110int eclfio_skip( FILE*, const char* opts, int n );
111
112/*
113 * Get the next record, and its number of elements.
114 *
115 * The record buffer is generally assumed to be of approperiate size, which can
116 * be queried with eclfio_sizeof.
117 *
118 * On success, the value of recordsize denotes the number of elements read,
119 * whose size is determined by the "cifd" options. It is generally assumed that
120 * recordsize upon calling this function contains the size of the record
121 * buffer, as a failsafe mechanism - if a record is larger than this value, the
122 * read will be aborted and the file position rolled back. To opt out of this
123 * check, add # to opts.
124 *
125 * Both recordsize and record can be NULL, in which case the number of elements
126 * read is not returned, and no data is returned respectively. This allows
127 * precise reporting on how many elements each skipped records contains.
128 *
129 * If the elementsize is larger than 1, and transformation has not been
130 * explicitly disabled, endianness will be converted appropriately.
131 *
132 * It is assumed that all record has an appropriate head and tail. If it is
133 * know that no record has a tail, force this by passing ~ in opts. However, if
134 * it is uncertain if all records has tails, or it's alternating between tail
135 * and no-tail, the $ option tries to recover from missing tails by assuming
136 * the current position is the start of the next record.
137 *
138 * The contents of record* is unspecified in case of read failures, and may not
139 * be relied upon. If the function returns ECL_EINVAL, the output record is
140 * untouched.
141 *
142 * This function returns ECL_OK upon success, ECL_ERR_READ in case of read- or
143 * seek errors, ECL_INVALID_RECORD if either the record tail is broken and
144 * options is set accordingly. The list of error codes is not exhaustive, and
145 * robust code should have fallthrough error handling cases.
146 */
147int eclfio_get( FILE*, const char* opts, int32_t* recordsize, void* record );
148
149/*
150 * Put a record of nmemb elements
151 *
152 * This function will write both head and tail, unless tail writing is
153 * explicitly disabled with ~. If (nmemb * elemsize) overflows int32, the write
154 * is aborted and ECL_EINVAL is returned.
155 *
156 * put largely follows the same rules as get, including those of endianness.
157 * The file pointer is rolled back if any part of the function should fail, as
158 * if the function was never called.
159 *
160 * If a write fails after partial writes, no attempts are made to roll back
161 * written changes.
162 *
163 * Returns ECL_OK on success, or ECL_ERR_WRITE on failure. If ECL_ERR_SEEK is
164 * returned, the integrity of the file stream can not be guaranteed, and its
165 * state is considered unspecified.
166 */
167int eclfio_put( FILE*, const char* opts, int nmemb, const void* );
168
177};
178
179#ifdef __cplusplus
180}
181#endif
182
183#endif //ECL_FORTIO_H
int eclfio_get(FILE *, const char *opts, int32_t *recordsize, void *record)
ecl_errno
Definition: e3/ecl/fortio.h:169
@ ECL_OK
Definition: e3/ecl/fortio.h:170
@ ECL_ERR_SEEK
Definition: e3/ecl/fortio.h:172
@ ECL_ERR_READ
Definition: e3/ecl/fortio.h:173
@ ECL_ERR_WRITE
Definition: e3/ecl/fortio.h:174
@ ECL_ERR_UNKNOWN
Definition: e3/ecl/fortio.h:171
@ ECL_EINVAL
Definition: e3/ecl/fortio.h:176
@ ECL_INVALID_RECORD
Definition: e3/ecl/fortio.h:175
int eclfio_skip(FILE *, const char *opts, int n)
int eclfio_sizeof(FILE *, const char *opts, int32_t *out)
int eclfio_put(FILE *, const char *opts, int nmemb, const void *)