-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfdf_file.c
452 lines (352 loc) · 11.4 KB
/
fdf_file.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
#include "fdf_file.h"
#include "fdf_data_types.h"
#include "fdf_types_full.h"
#include <assert.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/* Magic header for file type and verification of integrity */
static const char magic_cookie[24] = { '@', 'F', 'D', 'F',
13, 37, 0, 0,
'F', 'D', 'F', 'D',
'F', 'D', 'F', 'D',
'F', 'D', 'F', 'D',
'F', 'D', 'F', '@' };
static const char fdf_version[8] = { 0, 0, 0, 1, 0, 0, 0, 2 };
struct checksum_uint128 {
uint64_t s1, s2;
};
/**
\brief The header that is present in each file.
The header is 64 bits in size.
*/
struct fdf_header {
/** The magic header. */
char magic[24]; /* offset 0, 40 bits left. */
/** Version number, first 4 bits are major, last 4 bits are minor. */
char version[8]; /* offset 24, 32 bits left. */
/** File size in bytes */
uint64_t file_size; /* offset 32, 24 bits left. */
/** MD5 Checksum */
struct checksum_uint128 checksum; /* offset 40, 8 bits left. */
/** Number of data blocks. */
uint32_t n_blocks; /* offset 56, 4 bits left. */
/** These are reserved for future use/unused.
Extension can always happen via the UTF-8 way, where
the last bit of unused will flag whether or not more
headers follow. Unused is guaranteed to be 0 for a 64 bit header.
for now is set to all zeros.
*/
char unused[4]; /* offset 60, 4 bits left */
};
unsigned int fdf_write_file_meta( fdf_file *f, int *status );
void fdf_close( fdf_file *bm )
{
/* Write some pending info to the header of the file if the file was
opened in write or append mode. */
if( bm->open_mode == FDF_WRITE_ONLY ||
bm->open_mode == FDF_APPEND_ONLY ){
int status = FDF_SUCCESS;
fdf_write_file_meta( bm, &status );
}
fclose(bm->f);
free(bm);
}
fdf_file *fdf_open( const char *fname, int mode, int *status )
{
fdf_file *bm = malloc( sizeof( fdf_file ) );
bm->f = NULL;
if( mode == FDF_READ_ONLY ){
bm->f = fopen( fname, "rb" );
if( !bm->f ){
*status = FDF_OPEN_FOR_READ_FAILED;
}else if( (*status = fdf_verify_header( bm )) != FDF_SUCCESS ){
fclose( bm->f );
bm->f = NULL;
}else{
/* AOK. */
*status = FDF_SUCCESS;
}
}else if( mode == FDF_WRITE_ONLY ){
bm->f = fopen( fname, "wb" );
if( !bm->f ){
*status = FDF_OPEN_FOR_WRITE_FAILED;
}else{
*status = FDF_SUCCESS;
}
}else{
*status = FDF_UNKNOWN_WRITE_MODE;
}
bm->bytes_written = 0;
bm->blocks_written = 0;
bm->open_mode = mode;
return bm;
}
unsigned int fdf_write_header( fdf_file *fdf_f )
{
struct fdf_header my_header;
/*
fprintf( stderr, "Writing header to file at %p\n", (void*)fdf_f->f );
fprintf( stderr, "Offsets of members are:\n" );
fprintf( stderr, " magic: %lu\n" , offsetof(struct fdf_header, magic));
fprintf( stderr, " version: %lu\n" , offsetof(struct fdf_header, version));
fprintf( stderr, " file_size: %lu\n", offsetof(struct fdf_header, file_size));
fprintf( stderr, " checksum: %lu\n", offsetof(struct fdf_header, checksum));
fprintf( stderr, " n_blocks: %lu\n", offsetof(struct fdf_header, n_blocks));
fprintf( stderr, " unused: %lu\n", offsetof(struct fdf_header, unused));
*/
memcpy( my_header.magic, magic_cookie,
sizeof(magic_cookie)/sizeof(char) );
memcpy( my_header.version, fdf_version,
sizeof(fdf_version)/sizeof(char) );
/* Now figure out how you can seek back to the following bits
to write checksums and the like... */
my_header.n_blocks = 0;
my_header.file_size = 8; /* Initial file size is 8 bytes. */
my_header.checksum.s1 = 0;
my_header.checksum.s2 = 0;
my_header.unused[0] = 0;
my_header.unused[1] = 0;
my_header.unused[2] = 0;
my_header.unused[3] = 0;
unsigned int size_write = sizeof(struct fdf_header);
unsigned int n_written = fwrite( &my_header, size_write, 1, fdf_f->f );
assert( n_written * size_write == 8*my_header.file_size &&
"Programmer's assumption on file size incorrect!" );
int bytes = n_written * size_write;
fdf_f->bytes_written += bytes;
return bytes;
}
unsigned int fdf_read_header( fdf_file *fdf_f, struct fdf_header *header )
{
unsigned int size_read = sizeof(struct fdf_header);
unsigned int n_read = fread( header, size_read, 1, fdf_f->f );
return n_read * size_read;
}
int fdf_verify_header( fdf_file *fdf_f )
{
struct fdf_header my_header;
int bytes_read = fdf_read_header( fdf_f, &my_header );
if( bytes_read != sizeof(struct fdf_header) ){
return FDF_READ_BYTES_MISMATCH;
}
/* Various checks on the header: */
if( fdf_verify_header_cookie( &my_header ) == 0 ){
return FDF_CORRUPT_OR_NO_HEADER;
}
return FDF_SUCCESS;
}
int fdf_verify_header_cookie( const void *header )
{
const struct fdf_header *my_header = header;
for( int i = 0; i < sizeof(my_header->magic); ++i ){
if( my_header->magic[i] != magic_cookie[i] ){
return 0;
}
}
return 1;
}
unsigned int fdf_write_template( fdf_file *f, const fdf_template *templ )
{
unsigned int tot_bytes = 0;
tot_bytes = fdf_write_header( f );
long unsigned int header_size = sizeof(struct fdf_header);
long unsigned int template_size = sizeof(struct fdf_template);
assert( tot_bytes == header_size && "Byte write mismatch!" );
int n_written = fwrite( templ, sizeof(fdf_template), 1, f->f );
unsigned int bytes_template = n_written * sizeof(fdf_template);
tot_bytes += bytes_template;
assert( tot_bytes == header_size + template_size
&& "Byte write mismatch!" );
f->bytes_written += bytes_template;
return tot_bytes;
}
unsigned int fdf_write_time( fdf_file *f, const fdf_template *templ,
void *tstamp )
{
unsigned int size_write = fdf_data_type_to_rw_size( templ->time_type );
unsigned int n_written = fwrite( tstamp, size_write, 1, f->f );
assert( n_written == 1 &&
"Byte write mismatch!" );
unsigned int bytes = n_written * size_write;
f->bytes_written += bytes;
return bytes;
}
unsigned int fdf_write_grid_meta( fdf_file *f, const fdf_grid_meta *grid_spec )
{
unsigned int data_type = grid_spec->type;
int Nx = grid_spec->size;
assert( fdf_verify_data_type( data_type ) &&
"Unknown data type!" );
unsigned int n_written = fwrite( &data_type, sizeof(unsigned int), 1, f->f );
unsigned int tot_bytes = 0;
assert( n_written == 1 &&
"Byte write mismatch!" );
tot_bytes += n_written*sizeof(int);
n_written = fwrite( &Nx, sizeof(int), 1, f->f );
assert( n_written == 1 &&
"Byte write mismatch!" );
tot_bytes += n_written*sizeof(unsigned int);
f->bytes_written += tot_bytes;
return tot_bytes;
}
unsigned int fdf_write_grid_data( fdf_file *f, const fdf_grid_meta *grid_spec,
void *grid )
{
unsigned int size_write = fdf_data_type_to_rw_size( grid_spec->type );
int n_written = fwrite( grid, size_write, grid_spec->size, f->f );
assert( n_written == grid_spec->size && "Byte write mismatch!" );
unsigned int tot_bytes = n_written * size_write;
f->bytes_written += tot_bytes;
return tot_bytes;
}
int fdf_read_template( fdf_file *f, fdf_template *templ )
{
unsigned int n_read = fread( templ, sizeof(fdf_template), 1, f->f );
if( n_read != 1 ){
return FDF_READ_BYTES_MISMATCH;
}else{
return FDF_SUCCESS;
}
}
int fdf_read_time( fdf_file *f, const fdf_template *templ, void *tstamp )
{
unsigned int size_read = fdf_data_type_to_rw_size( templ->time_type );
unsigned int n_read = fread( tstamp, size_read, 1, f->f );
return n_read;
}
int fdf_read_grid_meta( fdf_file *f, fdf_grid_meta *grid_specs )
{
int n_read = fread( grid_specs, sizeof(fdf_grid_meta), 1, f->f );
assert( n_read == 1 && "Byte read mismatch!" );
return FDF_SUCCESS;
}
int fdf_read_grid_data( fdf_file *f, const fdf_grid_meta *grid_specs,
void *grid )
{
unsigned int size_read = fdf_data_type_to_rw_size( grid_specs->type );
int n_read = fread( grid, size_read, grid_specs->size, f->f );
assert( n_read == grid_specs->size && "Byte read mismatch!" );
return FDF_SUCCESS;
}
int fdf_read_data_raw( fdf_file *f, const fdf_template *templ,
const fdf_grid_meta *grid_specs, void *data )
{
unsigned int size_read = fdf_data_type_to_rw_size( templ->data_type );
int n_elements = grid_specs[0].size;
if( templ->dimension == 2 ){
n_elements *= grid_specs[1].size;
}
int n_read = fread( data, size_read, n_elements, f->f );
assert( n_read == n_elements && "Byte read mismatch!" );
return n_read;
}
unsigned int fdf_write_data_raw( fdf_file *f, const fdf_template *templ,
const fdf_grid_meta *grid_specs, void *data )
{
unsigned int size_write = fdf_data_type_to_rw_size( templ->data_type );
int n_elements = grid_specs[0].size;
if( templ->dimension == 2 ){
n_elements *= grid_specs[1].size;
}
int n_write = fwrite( data, size_write, n_elements, f->f );
assert( n_write == n_elements && "Byte read mismatch!" );
f->bytes_written += n_elements*size_write;
return n_elements*size_write;
}
unsigned int fdf_write_data_block( fdf_file *f, const fdf_template *templ,
const fdf_grid_meta *grid_specs,
void *time, void *data )
{
unsigned int bytes = 0;
bytes += fdf_write_time( f, templ, time );
bytes += fdf_write_data_raw( f, templ, grid_specs, data );
f->blocks_written++;
return bytes;
}
int fdf_read_data_block( fdf_file *f, const fdf_template *templ,
const fdf_grid_meta *grid_specs,
void *time, void *data )
{
int n_read = 0;
n_read += fdf_read_time( f, templ, time );
if( n_read != 1 ){
return 0;
}
int n_expect = grid_specs[0].size;
if( templ->dimension == 2 ){
n_expect *= grid_specs[1].size;
}
n_read += fdf_read_data_raw( f, templ, grid_specs, data );
if( n_read != n_expect + 1 ){
return 0;
}
return n_read;
}
int fdf_getpos( fdf_file *f, fpos_t *pos )
{
return fgetpos( f->f, pos );
}
int fdf_jump_to_pos( fdf_file *f, const fpos_t *pos )
{
return fsetpos( f->f, pos );
}
int fdf_jump_to_n_blocks( fdf_file *f )
{
return fseek( f->f, 24+8+8+16, SEEK_SET );
}
int fdf_jump_to_file_size( fdf_file *f )
{
return fseek( f->f, 24+8, SEEK_SET );
}
int fdf_jump_to_checksum( fdf_file *f )
{
return fseek( f->f, 24+8+8, SEEK_SET );
}
void fdf_calc_checksum( fdf_file *f, struct checksum_uint128 *chk )
{
chk->s1 = 0;
chk->s2 = 0;
}
void print_bitmask( FILE *f, uint64_t number )
{
int shift = 0;
int current_bit = 1;
while( shift < 63 ){
int c = '0';
if( number & current_bit ){
c = '1';
}
fputc( c, f );
++shift;
current_bit = 1 << shift;
}
}
void print_hexmask( FILE *f, uint64_t number )
{
unsigned char *byte = (void*)(&number);
for( int bytes = 0; bytes < 8; ++bytes ){
fprintf( f, "%x ", *(byte + bytes) );
}
}
unsigned int fdf_write_file_meta( fdf_file *f, int *status )
{
/* It is easiest to jump back to top and work your way down. */
fpos_t end_of_file;
*status = fdf_getpos( f, &end_of_file );
if( *status ){
return 0;
}
fdf_jump_to_file_size( f );
int bytes = fwrite( &f->bytes_written, sizeof(uint64_t), 1, f->f );
struct checksum_uint128 checksum;
fdf_calc_checksum( f, &checksum );
/* Do NOT count these in bytes written because they
overwrite data. */
bytes += fwrite( &checksum, sizeof(checksum), 1, f->f );
bytes += fwrite( &f->blocks_written, sizeof(uint32_t), 1, f->f );
fdf_jump_to_pos( f, &end_of_file );
return bytes;
}