1 /*============================================================================
2  * Base functions for writing Kernel I/O files.
3  *============================================================================*/
4 
5 /*
6   This file is part of Code_Saturne, a general-purpose CFD tool.
7 
8   Copyright (C) 1998-2021 EDF S.A.
9 
10   This program is free software; you can redistribute it and/or modify it under
11   the terms of the GNU General Public License as published by the Free Software
12   Foundation; either version 2 of the License, or (at your option) any later
13   version.
14 
15   This program is distributed in the hope that it will be useful, but WITHOUT
16   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17   FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18   details.
19 
20   You should have received a copy of the GNU General Public License along with
21   this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
22   Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24 
25 /*----------------------------------------------------------------------------*/
26 
27 #include "ecs_def.h"
28 
29 /*----------------------------------------------------------------------------
30  * Standard C library headers
31  *----------------------------------------------------------------------------*/
32 
33 #include <assert.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 
39 /*----------------------------------------------------------------------------
40  *  Local headers
41  *----------------------------------------------------------------------------*/
42 
43 #include "ecs_def.h"
44 #include "ecs_file.h"
45 #include "ecs_mem.h"
46 
47 /*----------------------------------------------------------------------------
48  *  Headers for the current file
49  *----------------------------------------------------------------------------*/
50 
51 #include "ecs_comm.h"
52 
53 /*----------------------------------------------------------------------------*/
54 
55 BEGIN_C_DECLS
56 
57 /*=============================================================================
58  * Macro definitions
59  *============================================================================*/
60 
61 /*=============================================================================
62  * Local type definitions
63  *============================================================================*/
64 
65 /*----------------------------------------------------------------------------
66  * Structure defining an output writer
67  *----------------------------------------------------------------------------*/
68 
69 struct _ecs_comm_t {
70 
71   char           *name;             /* Writer name */
72 
73   ecs_file_t     *f;                /* Associated file structure pointer */
74 
75   size_t          header_size;      /* Header default size */
76   size_t          header_align;     /* Header alignment */
77   size_t          body_align;       /* Body alignment */
78 
79   size_t          cur_pos;          /* Current position in file */
80 
81 };
82 
83 /*============================================================================
84  * Local function defintions
85  *============================================================================*/
86 
87 /*----------------------------------------------------------------------------
88  * Write padding zeroes to ensure following alignement if necessary
89  *----------------------------------------------------------------------------*/
90 
91 static void
_write_pad(ecs_comm_t * comm,size_t alignment)92 _write_pad(ecs_comm_t  *comm,
93            size_t       alignment)
94 {
95   size_t pad_size = (alignment - (comm->cur_pos % alignment)) % alignment;
96 
97   if (pad_size > 0) {
98 
99     char padding[128] = "";
100     size_t rem_size = pad_size;
101 
102     memset(padding, 0, sizeof(padding));
103 
104     while (rem_size > 0) {
105       size_t write_size = ECS_MIN(rem_size, sizeof(padding));
106       ecs_file_write(padding, 1, write_size, comm->f);
107       rem_size -= write_size;
108     }
109 
110     comm->cur_pos += pad_size;
111   }
112 }
113 
114 /*----------------------------------------------------------------------------
115  * Write a record to the interface file
116  *----------------------------------------------------------------------------*/
117 
118 static void
_write_rec(ecs_comm_t * comm,const void * rec,size_t n_elts,ecs_type_t datatype)119 _write_rec(ecs_comm_t  *comm,
120            const void  *rec,
121            size_t       n_elts,
122            ecs_type_t   datatype)
123 {
124   size_t t_size = 0;
125 
126   assert(comm != NULL);
127   assert(rec  != NULL);
128 
129   /* Determine size associated with type */
130 
131   switch (datatype) {
132   case ECS_TYPE_ecs_int_t:
133     t_size = sizeof(ecs_int_t);
134     break;
135   case ECS_TYPE_ecs_coord_t:
136     t_size = sizeof(ecs_coord_t);
137     break;
138   case ECS_TYPE_ecs_size_t:
139     t_size = sizeof(ecs_size_t);
140     break;
141   case ECS_TYPE_char:
142     t_size = sizeof(char);
143     break;
144   case ECS_TYPE_size_t:
145     t_size = 8;
146     break;
147   default:
148     assert(   datatype == ECS_TYPE_ecs_int_t
149            || datatype == ECS_TYPE_ecs_coord_t
150            || datatype == ECS_TYPE_ecs_size_t
151            || datatype == ECS_TYPE_size_t
152            || datatype == ECS_TYPE_char);
153   }
154 
155   /* write record to file */
156   /*----------------------*/
157 
158   if (datatype != ECS_TYPE_size_t || sizeof(size_t) == 8) {
159     ecs_file_write(rec, t_size, n_elts, comm->f);
160     comm->cur_pos += n_elts*t_size;
161   }
162 
163   else { /* if (datatype == ECS_TYPE_size_t && sizeof(size_t) != 8) */
164 
165     size_t i;
166 
167     assert(sizeof(unsigned long long) == 8);
168 
169     for (i = 0; i < n_elts; i++) {
170       const unsigned char *rp = rec;
171       unsigned long long _rec = *((const size_t *)(rp + i*sizeof(size_t)));
172       assert(sizeof(long long) == 8);
173       ecs_file_write(&_rec, 8, 1, comm->f);
174       comm->cur_pos += 8;
175     }
176 
177   }
178 }
179 
180 /*----------------------------------------------------------------------------
181  * Open output file and write base file description headers
182  *----------------------------------------------------------------------------*/
183 
184 static void
_file_open(ecs_comm_t * comm,const char * file_name)185 _file_open(ecs_comm_t  *comm,
186            const char  *file_name)
187 {
188   char header[64] = "";
189   size_t sizes[3] = {comm->header_size,
190                      comm->header_align,
191                      comm->body_align};
192 
193   memset(header, 0, 64);
194 
195   /* BE stands for big-endian, allowing for future
196      native file mode generation, using BE/LE */
197 
198   strncpy(header, "Code_Saturne I/O, BE, R0", 63);
199 
200   /* Open file */
201 
202   comm->f = ecs_file_open(file_name,
203                           ECS_FILE_MODE_WRITE,
204                           ECS_FILE_TYPE_BINARY);
205 
206   ecs_file_set_big_endian(comm->f);
207 
208   /* Write header and comment information */
209 
210   _write_rec(comm, header, 64, ECS_TYPE_char);
211 
212   memset(header, 0, 64);
213   strncpy(header, "Face-based mesh definition, R0", 63);
214 
215   _write_rec(comm, header, 64, ECS_TYPE_char);
216 
217   _write_rec(comm, sizes, 3, ECS_TYPE_size_t);
218 }
219 
220 /*----------------------------------------------------------------------------
221  * Echo section output data
222  *----------------------------------------------------------------------------*/
223 
224 static void
_echo_header(const char * name,size_t n_values,const char * datatype_name)225 _echo_header(const char   *name,
226              size_t        n_values,
227              const char   *datatype_name)
228 {
229   char name_pad[33] = "";
230   char type_pad[3] = "";
231 
232   if (strlen(name) < 32) {
233     memset(name_pad, ' ', sizeof(name_pad));
234     name_pad[32 - strlen(name)] = '\0';
235   }
236   if (strlen(datatype_name) < 2) {
237     type_pad[0] = ' '; type_pad[1] = ' ';
238     type_pad[2 - strlen(datatype_name)] = '\0';
239   }
240 
241   if (n_values > 0)
242     printf(_("  Wrote: \"%s\"%s; Type: \"%s\"%s; Size: %lu\n"),
243            name, name_pad, datatype_name, type_pad,
244            (unsigned long)n_values);
245 
246   else
247     printf(_("  Wrote: \"%s\"\n"),
248            name);
249 
250   fflush(stdout);
251 }
252 
253 /*============================================================================
254  * Public function definitions
255  *============================================================================*/
256 
257 ecs_comm_t *
ecs_comm_initialize(const char * file_name)258 ecs_comm_initialize(const char  *file_name)
259 {
260   ecs_comm_t  * comm = NULL;
261 
262   /* Create structure */
263 
264   ECS_MALLOC(comm, 1, ecs_comm_t);
265 
266   ECS_MALLOC(comm->name, strlen(file_name) + 1, char);
267 
268   strcpy(comm->name, file_name);
269 
270   /* Initialize other fields */
271 
272   comm->header_size = 96;
273   comm->header_align = 64;
274   comm->body_align = 64;
275   comm->cur_pos = 0;
276 
277   comm->f  = NULL;
278 
279   /* Jump a line in log file */
280 
281   printf("\n");
282 
283   /* Info on interface creation */
284 
285   printf(_("  Opening file: %s\n"),
286          comm->name);
287 
288   printf("\n");
289 
290   fflush(stdout);
291 
292   /* Create file descriptor */
293 
294   _file_open(comm, file_name);
295 
296   return comm;
297 }
298 
299 /*----------------------------------------------------------------------------
300  * Close writer.
301  *
302  * arguments:
303  *   comm <-- pointer to writer structure pointer
304  *----------------------------------------------------------------------------*/
305 
306 void
ecs_comm_finalize(ecs_comm_t ** comm)307 ecs_comm_finalize(ecs_comm_t **comm)
308 {
309   ecs_comm_t *_comm = *comm;
310 
311   printf("\n");
312 
313   printf(_("  Closing file: %s\n"),
314          _comm->name);
315 
316   if (_comm->f != NULL)
317     _comm->f = ecs_file_free(_comm->f);
318 
319   ECS_FREE(_comm->name);
320 
321   ECS_FREE(_comm);
322 
323   *comm = _comm;
324 }
325 
326 /*----------------------------------------------------------------------------
327  * Write a section to the Kernel I/O file.
328  *
329  * Grid locations and possibly indexes may be assigned to a section by
330  * specifying a location id; the first time a given location id appears in
331  * the file is considered a declaration. In the same manner, an index id
332  * may be specified. Values of zero indicate no location or index base
333  * is used. It is up to the calling code to ensure that total number of
334  * values, location size, and number of values per location are consistent,
335  * as this my be important for code reading the file.
336  *
337  * arguments:
338  *   name              <-- section name
339  *   location_id       <-- id of associated location
340  *   index_id          <-- id of associated index
341  *   n_location_values <-- number of values per location
342  *   embed             <-- embed values in header
343  *   values            <-- values to write
344  *   value_type        <-- type of value to write
345  *   comm              <-- Kernel I/O file output structure
346  *----------------------------------------------------------------------------*/
347 
348 void
ecs_comm_write_section(const char * name,size_t n_values,size_t location_id,size_t index_id,size_t n_location_values,bool embed,const void * values,ecs_type_t value_type,ecs_comm_t * comm)349 ecs_comm_write_section(const char  *name,
350                        size_t       n_values,
351                        size_t       location_id,
352                        size_t       index_id,
353                        size_t       n_location_values,
354                        bool         embed,
355                        const void  *values,
356                        ecs_type_t   value_type,
357                        ecs_comm_t  *comm)
358 {
359   /* Section is defined by:
360 
361      section size (in bytes): 8 bytes
362      n_elts, location_id, index_id, n_location_values: 4*8 bytes
363      name_size (including padding to 8 byte alignment): 8 bytes
364      datatype_name: 8 bytes (bit 6 = '\0', bit 7 = embed flag)
365      section name: strlen(name) + 1 + padding to 8 bytes
366      optional embedded data: n_values*value_type_size
367   */
368 
369   size_t header_sizes[6] = {56,       /* 6*8 +8 */
370                             n_values,
371                             location_id,
372                             index_id,
373                             n_location_values,
374                             0};
375   size_t name_size = 0;
376   size_t name_pad_size = 0;
377   size_t data_size = 0;
378   char   datatype_name[8];
379   char   name_pad[8];
380 
381   assert(comm != NULL);
382   assert(name != NULL);
383 
384   /* Initialization */
385 
386   memset(datatype_name, 0, sizeof(datatype_name));
387   memset(name_pad, 0, sizeof(name_pad));
388 
389   /* Section name */
390 
391   name_size = strlen(name);
392   name_pad_size = 8-(name_size%8); /* At least 1 NULL
393                                       character with this rule */
394 
395   header_sizes[0] += (name_size + name_pad_size);
396   header_sizes[5] = (name_size + name_pad_size);
397 
398   /* Value type name */
399 
400   if (n_values > 0) {
401 
402     switch(value_type) {
403 
404     case ECS_TYPE_ecs_int_t:
405 
406       switch(sizeof(ecs_int_t)) {
407       case 4:
408         strcpy(datatype_name, "i4");
409         break;
410       case 8:
411         strcpy(datatype_name, "i8");
412         break;
413       default:
414         assert(sizeof(ecs_int_t) == 4 || sizeof(ecs_int_t) == 8);
415       }
416       break;
417 
418     case ECS_TYPE_ecs_coord_t:
419 
420       switch(sizeof(ecs_coord_t)) {
421       case 4:
422         strcpy(datatype_name, "r4");
423         break;
424       case 8:
425         strcpy(datatype_name, "r8");
426         break;
427       default:
428         assert(sizeof(ecs_coord_t) == 4 || sizeof(ecs_coord_t) == 8);
429       }
430       break;
431 
432     case ECS_TYPE_ecs_size_t:
433 
434       switch(sizeof(ecs_size_t)) {
435       case 4:
436         strcpy(datatype_name, "u4");
437         break;
438       case 8:
439         strcpy(datatype_name, "u8");
440         break;
441       default:
442         assert(sizeof(ecs_size_t) == 4 || sizeof(ecs_size_t) == 8);
443       }
444       break;
445 
446     case ECS_TYPE_size_t:
447 
448       strcpy(datatype_name, "u8");
449       break;
450 
451     case ECS_TYPE_char:
452 
453       strcpy(datatype_name, "c ");
454       break;
455 
456     default:
457 
458       assert(   value_type == ECS_TYPE_ecs_int_t
459              || value_type == ECS_TYPE_ecs_coord_t
460              || value_type == ECS_TYPE_ecs_size_t
461              || value_type == ECS_TYPE_size_t
462              || value_type == ECS_TYPE_char);
463 
464 
465     } /* End of switch on value_type */
466 
467   }
468 
469   if (embed == true) {
470 
471     datatype_name[7] = 'e';
472 
473     if (datatype_name[1] == '4')
474       data_size = 4*n_values;
475     else if (datatype_name[1] == '8')
476       data_size = 8*n_values;
477     else
478       data_size = n_values;
479 
480     header_sizes[0] += data_size;
481 
482   }
483 
484   /* Only output data if file exists */
485 
486   if (comm->f != NULL) {
487 
488     /* Align if necessary */
489 
490     _write_pad(comm, comm->header_align);
491 
492     /* Write sizes */
493 
494     _write_rec(comm, header_sizes, 6, ECS_TYPE_size_t);
495 
496     /* Type information */
497 
498     _write_rec(comm, datatype_name, 8, ECS_TYPE_char);
499 
500     /* Section name */
501 
502     _write_rec(comm, name, name_size, ECS_TYPE_char);
503     _write_rec(comm, name_pad, name_pad_size, ECS_TYPE_char);
504 
505     /* Embedded values */
506 
507     if (n_values > 0 && embed == true)
508       _write_rec(comm, values, n_values, value_type);
509 
510     /* Ensure header is at least the size of the basic header block size,
511        as reads may read at least this much data */
512 
513     if (header_sizes[0] < comm->header_size) {
514       char   header_pad[64];
515       size_t header_pad_size = comm->header_size - header_sizes[0];
516       memset(header_pad, 0, sizeof(header_pad));
517       while (header_pad_size > 0) {
518         size_t write_size = ECS_MIN(header_pad_size, sizeof(header_pad));
519         ecs_file_write(header_pad, 1, write_size, comm->f);
520         header_pad_size -= write_size;
521         comm->cur_pos += write_size;
522       }
523     }
524 
525     /* If values are not embedded, handle separate value block */
526 
527     if (n_values > 0 && embed == false) {
528 
529       _write_pad(comm, comm->body_align);
530       _write_rec(comm, values, n_values, value_type);
531 
532     }
533 
534   }
535 
536   /* Print message */
537 
538   _echo_header(name, n_values, datatype_name);
539 }
540 
541 /*----------------------------------------------------------------------------*/
542 
543 END_C_DECLS
544 
545