1 /*********************************************************************
2 data -- Structure and functions to represent/work with data
3 This is part of GNU Astronomy Utilities (Gnuastro) package.
4 
5 Original author:
6      Mohammad Akhlaghi <mohammad@akhlaghi.org>
7 Contributing author(s):
8 Copyright (C) 2016-2021, Free Software Foundation, Inc.
9 
10 Gnuastro is free software: you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by the
12 Free Software Foundation, either version 3 of the License, or (at your
13 option) any later version.
14 
15 Gnuastro is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
22 **********************************************************************/
23 #include <config.h>
24 
25 #include <errno.h>
26 #include <error.h>
27 #include <fcntl.h>
28 #include <float.h>
29 #include <ctype.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <dirent.h>
33 #include <inttypes.h>
34 
35 #include <gnuastro/wcs.h>
36 #include <gnuastro/data.h>
37 #include <gnuastro/tile.h>
38 #include <gnuastro/blank.h>
39 #include <gnuastro/table.h>
40 #include <gnuastro/pointer.h>
41 
42 #include <gnuastro-internal/checkset.h>
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 /*********************************************************************/
64 /*************              Allocation             *******************/
65 /*********************************************************************/
66 /* Allocate a data structure based on the given parameters. If you want to
67    force the array into the hdd/ssd (mmap it), then set minmapsize=-1
68    (largest possible size_t value), in this way, no file will be larger. */
69 gal_data_t *
gal_data_alloc(void * array,uint8_t type,size_t ndim,size_t * dsize,struct wcsprm * wcs,int clear,size_t minmapsize,int quietmmap,char * name,char * unit,char * comment)70 gal_data_alloc(void *array, uint8_t type, size_t ndim, size_t *dsize,
71                struct wcsprm *wcs, int clear, size_t minmapsize,
72                int quietmmap, char *name, char *unit, char *comment)
73 {
74   gal_data_t *out;
75 
76   /* Allocate the space for the actual structure. */
77   errno=0;
78   out=malloc(sizeof *out);
79   if(out==NULL)
80     error(EXIT_FAILURE, errno, "%s: %zu bytes for gal_data_t",
81           __func__, sizeof *out);
82 
83   /* Initialize the allocated array. */
84   gal_data_initialize(out, array, type, ndim, dsize, wcs, clear, minmapsize,
85                       quietmmap, name, unit, comment);
86 
87   /* Return the final structure. */
88   return out;
89 }
90 
91 
92 
93 
94 
95 /* Initialize the data structure.
96 
97    Some notes:
98 
99    - The 'status' value is the only element that cannot be set by this
100      function, it is initialized to zero.
101 
102    - If no 'array' is given, a blank array of the given size will be
103      allocated. If it is given the array pointer will be directly put here,
104      so do not free it independently any more. If you want a separate copy
105      of a dataset, you should use 'gal_data_copy', not this function.
106 
107    - Space for the 'name', 'unit', and 'comment' strings within the data
108      structure are allocated here. So you can safely use literal strings,
109      or statically allocated ones, or simply the strings from other data
110      structures (and not have to worry about which one to free later).
111 */
112 void
gal_data_initialize(gal_data_t * data,void * array,uint8_t type,size_t ndim,size_t * dsize,struct wcsprm * wcs,int clear,size_t minmapsize,int quietmmap,char * name,char * unit,char * comment)113 gal_data_initialize(gal_data_t *data, void *array, uint8_t type,
114                     size_t ndim, size_t *dsize, struct wcsprm *wcs,
115                     int clear, size_t minmapsize, int quietmmap,
116                     char *name, char *unit, char *comment)
117 {
118   size_t i;
119   size_t data_size_limit = (size_t)(-1);
120 
121   /* Do the simple copying cases. For the display elements, set them all to
122      impossible (negative) values so if not explicitly set by later steps,
123      the default values are used if/when printing.*/
124   data->flag       = 0;
125   data->status     = 0;
126   data->next       = NULL;
127   data->ndim       = ndim;
128   data->type       = type;
129   data->block      = NULL;
130   data->mmapname   = NULL;
131   data->quietmmap  = quietmmap;
132   data->minmapsize = minmapsize;
133   gal_checkset_allocate_copy(name, &data->name);
134   gal_checkset_allocate_copy(unit, &data->unit);
135   gal_checkset_allocate_copy(comment, &data->comment);
136   data->disp_fmt=data->disp_width=data->disp_precision=-1;
137 
138 
139   /* Copy the WCS structure. */
140   data->wcs=gal_wcs_copy(wcs);
141 
142 
143   /* Allocate space for the dsize array, only if the data are to have any
144      dimensions or size along the dimensions. Note that in our convention,
145      a number has a 'ndim=1' and 'dsize[0]=1', A 1D array also has
146      'ndim=1', but 'dsize[0]>1'. */
147   if(ndim && dsize)
148     {
149       /* Allocate dsize. */
150       errno=0;
151       data->dsize=malloc(ndim*sizeof *data->dsize);
152       if(data->dsize==NULL)
153         error(EXIT_FAILURE, errno, "%s: %zu bytes for data->dsize",
154               __func__, ndim*sizeof *data->dsize);
155 
156 
157       /* Fill in the 'dsize' array and in the meantime set 'size': */
158       data->size=1;
159       for(i=0;i<ndim;++i)
160         {
161           /* Size along a dimension cannot be 0 if we are in a
162              multi-dimensional dataset. In a single-dimensional dataset, we
163              can have an empty dataset. */
164           if(ndim>1 && dsize[i] == 0)
165             error(EXIT_FAILURE, 0, "%s: dsize[%zu]==0. The size of a "
166                   "dimension cannot be zero", __func__, i);
167 
168           /* Check for possible overflow while multiplying. */
169           if (dsize[i] >= data_size_limit / data->size)
170             error(EXIT_FAILURE, 0, "%s: dimension %zu size is too "
171                     "large %zu. Total is out of bounds",
172                     __func__, i, dsize[i]);
173 
174           /* Print a warning if the size in this dimension is too
175              large. May happen when the user (mistakenly) writes a negative
176              value in this dimension.. */
177           if (dsize[i] >= data_size_limit / 2)
178             fprintf(stderr, "%s: WARNING: dsize[%zu] value %zu is probably "
179                     "a mistake: it exceeds the limit %zu", __func__, i,
180                     dsize[i], data_size_limit / 2);
181 
182           /* Write this dimension's size, also correct the total number of
183              elements. */
184           data->size *= ( data->dsize[i] = dsize[i] );
185         }
186 
187 
188       /* Set the array pointer. If an non-NULL array pointer was given,
189          then use it. */
190       if(array)
191         data->array=array;
192       else
193         {
194           /* If a size wasn't given, just set a NULL pointer. */
195           if(data->size)
196             data->array=gal_pointer_allocate_ram_or_mmap(data->type,
197                                  data->size, clear, minmapsize,
198                                  &data->mmapname, quietmmap, __func__, "");
199           else data->array=NULL; /* The given size was zero! */
200         }
201     }
202   else
203     {
204       data->size=0;
205       data->array=NULL;
206       data->dsize=NULL;
207     }
208 }
209 
210 
211 
212 
213 
214 /* Free the allocated contents of a data structure, not the structure
215    itsself. The reason that this function is separate from 'gal_data_free'
216    is that the data structure might be allocated as an array (statically
217    like 'gal_data_t da[20]', or dynamically like 'gal_data_t *da;
218    da=malloc(20*sizeof *da);'). In both cases, a loop will be necessary to
219    delete the allocated contents of each element of the data structure
220    array, but not the structure its self. After that loop, if the array of
221    data structures was statically allocated, you don't have to do
222    anything. If it was dynamically allocated, we just have to run
223    'free(da)'.
224 
225    Since we aren't freeing the 'gal_data_t' its-self, after the allocated
226    space for each pointer is freed, the pointer is set to NULL for safety
227    (to avoid possible re-calls).
228 */
229 void
gal_data_free_contents(gal_data_t * data)230 gal_data_free_contents(gal_data_t *data)
231 {
232   size_t i;
233   char **strarr;
234 
235   if(data==NULL)
236     error(EXIT_FAILURE, 0, "%s: the input data structure to "
237           "'gal_data_free_contents' was a NULL pointer", __func__);
238 
239   /* Free all the possible allocations. */
240   if(data->name)    { free(data->name);    data->name    = NULL; }
241   if(data->unit)    { free(data->unit);    data->unit    = NULL; }
242   if(data->dsize)   { free(data->dsize);   data->dsize   = NULL; }
243   if(data->comment) { free(data->comment); data->comment = NULL; }
244   if(data->wcs)
245     { wcsfree(data->wcs); free(data->wcs); data->wcs     = NULL; }
246 
247   /* If the data type is string, then each element in the array is actually
248      a pointer to the array of characters, so free them before freeing the
249      actual array. */
250   if(data->type==GAL_TYPE_STRING && data->array)
251     {
252       strarr=data->array;
253       for(i=0;i<data->size;++i) if(strarr[i]) free(strarr[i]);
254     }
255 
256   /* Free the array (if it was separately allocated: not part of a block),
257      then set the 'array' to NULL. */
258   if(data->array && data->block==NULL)
259     {
260       if(data->mmapname)
261         gal_pointer_mmap_free(&data->mmapname, data->quietmmap);
262       else free(data->array);
263     }
264   data->array=NULL;
265 }
266 
267 
268 
269 
270 
271 /* Free the contents of the data structure and the data structure
272    itsself. */
273 void
gal_data_free(gal_data_t * data)274 gal_data_free(gal_data_t *data)
275 {
276   if(data)
277     {
278       gal_data_free_contents(data);
279       free(data);
280     }
281 }
282 
283 
284 
285 
286 
287 
288 
289 
290 
291 
292 
293 
294 
295 
296 
297 
298 
299 
300 
301 
302 /*********************************************************************/
303 /*************        Array of data structures      ******************/
304 /*********************************************************************/
305 /* Allocate an array of data structures and initialize all the values. */
306 gal_data_t *
gal_data_array_calloc(size_t size)307 gal_data_array_calloc(size_t size)
308 {
309   size_t i;
310   gal_data_t *out;
311 
312   /* Allocate the array to keep the structures. */
313   errno=0;
314   out=malloc(size*sizeof *out);
315   if(out==NULL)
316     error(EXIT_FAILURE, errno, "%s: %zu bytes for 'out'", __func__,
317           size*sizeof *out);
318 
319 
320   /* Set the pointers to NULL if they didn't exist and the non-pointers to
321      impossible integers (so the caller knows the array is only
322      allocated. 'minmapsize' should be set when allocating the array and
323      should be set when you run 'gal_data_initialize'. */
324   for(i=0;i<size;++i)
325     {
326       out[i].array      = NULL;
327       out[i].type       = GAL_TYPE_INVALID;
328       out[i].ndim       = 0;
329       out[i].dsize      = NULL;
330       out[i].size       = 0;
331       out[i].mmapname   = NULL;
332       out[i].minmapsize = -1;
333       out[i].quietmmap  = 1;
334       out[i].nwcs       = 0;
335       out[i].wcs        = NULL;
336       out[i].flag       = 0;
337       out[i].status     = 0;
338       out[i].next       = NULL;
339       out[i].block      = NULL;
340       out[i].name = out[i].unit = out[i].comment = NULL;
341       out[i].disp_fmt = out[i].disp_width = out[i].disp_precision = -1;
342     }
343 
344   /* Return the array pointer. */
345   return out;
346 }
347 
348 
349 
350 
351 
352 /* When you have an array of data structures. */
353 void
gal_data_array_free(gal_data_t * dataarr,size_t size,int free_array)354 gal_data_array_free(gal_data_t *dataarr, size_t size, int free_array)
355 {
356   size_t i;
357 
358   /* If its NULL, don't do anything. */
359   if(dataarr==NULL) return;
360 
361   /* First free all the contents. */
362   for(i=0;i<size;++i)
363     {
364       /* See if the array should be freed or not. */
365       if(free_array==0)
366         dataarr[i].array=NULL;
367 
368       /* Now clear the contents of the dataset. */
369       gal_data_free_contents(&dataarr[i]);
370     }
371 
372   /* Now you can free the whole array. */
373   free(dataarr);
374 }
375 
376 
377 
378 
379 
380 /* Create an array of gal_data_t pointers and initializes them. */
381 gal_data_t **
gal_data_array_ptr_calloc(size_t size)382 gal_data_array_ptr_calloc(size_t size)
383 {
384   size_t i;
385   gal_data_t **out;
386 
387   /* Allocate the array to keep the pointers. */
388   errno=0;
389   out=malloc(size*sizeof *out);
390   if(out==NULL)
391     error(EXIT_FAILURE, errno, "%s: %zu bytes for 'out'", __func__,
392           size*sizeof *out);
393 
394   /* Initialize all the pointers to NULL and return. */
395   for(i=0;i<size;++i) out[i]=NULL;
396   return out;
397 }
398 
399 
400 
401 
402 
403 /* Assuming that we have an array of pointers to data structures, this
404    function frees them. */
405 void
gal_data_array_ptr_free(gal_data_t ** dataptr,size_t size,int free_array)406 gal_data_array_ptr_free(gal_data_t **dataptr, size_t size, int free_array)
407 {
408   size_t i;
409   for(i=0;i<size;++i)
410     {
411       /* If the user doesn't want to free the array, it must be because
412          they are keeping its pointer somewhere else (that their own
413          responsability!), so we can just set it to NULL for the
414          'gal_data_free' to not free it. */
415       if(free_array==0)
416         dataptr[i]->array=NULL;
417 
418       /* Free this data structure. */
419       gal_data_free(dataptr[i]);
420     }
421 
422   /* Free the 'gal_data_t **'. */
423   free(dataptr);
424 }
425 
426 
427 
428 
429 
430 
431 
432 
433 
434 
435 
436 
437 
438 
439 
440 
441 
442 
443 
444 /*************************************************************
445  **************            Copying             ***************
446  *************************************************************/
447 /* Only to be used in 'data_copy_from_string'. */
448 static void
data_copy_to_string_not_parsed(char * string,void * to,uint8_t type)449 data_copy_to_string_not_parsed(char *string, void *to, uint8_t type)
450 {
451   if( strcmp(string, GAL_BLANK_STRING) )
452     gal_blank_write(to, type);
453   else
454     error(EXIT_FAILURE, 0, "%s: '%s' couldn't be parsed as '%s' type",
455           __func__, string, gal_type_name(type, 1));
456 }
457 
458 
459 
460 
461 
462 /* The 'from' array is an array of strings. We want to keep it as
463    numbers. Note that the case where both input and output structures are
464    string was */
465 static void
data_copy_from_string(gal_data_t * from,gal_data_t * to)466 data_copy_from_string(gal_data_t *from, gal_data_t *to)
467 {
468   size_t i;
469   void *ptr;
470   char **strarr=from->array, **outstrarr=to->array;
471 
472   /* Sanity check. */
473   if(from->type!=GAL_TYPE_STRING)
474     error(EXIT_FAILURE, 0, "%s: 'from' must have a string type.", __func__);
475   if(from->block)
476     error(EXIT_FAILURE, 0, "%s: tiles not currently supported ('block' "
477           "element must be NULL). Please contact us at %s so we can "
478           "implement this feature", __func__, PACKAGE_BUGREPORT);
479 
480   /* Do the copying. */
481   for(i=0;i<from->size;++i)
482     {
483       /* Set the pointer. */
484       switch(to->type)
485         {
486         case GAL_TYPE_UINT8:    ptr = (uint8_t *)(to->array)  + i;   break;
487         case GAL_TYPE_INT8:     ptr = (int8_t *)(to->array)   + i;   break;
488         case GAL_TYPE_UINT16:   ptr = (uint16_t *)(to->array) + i;   break;
489         case GAL_TYPE_INT16:    ptr = (int16_t *)(to->array)  + i;   break;
490         case GAL_TYPE_UINT32:   ptr = (uint32_t *)(to->array) + i;   break;
491         case GAL_TYPE_INT32:    ptr = (int32_t *)(to->array)  + i;   break;
492         case GAL_TYPE_UINT64:   ptr = (uint64_t *)(to->array) + i;   break;
493         case GAL_TYPE_INT64:    ptr = (int64_t *)(to->array)  + i;   break;
494         case GAL_TYPE_FLOAT32:  ptr = (float *)(to->array)    + i;   break;
495         case GAL_TYPE_FLOAT64:  ptr = (double *)(to->array)   + i;   break;
496         case GAL_TYPE_BIT:
497         case GAL_TYPE_STRLL:
498         case GAL_TYPE_COMPLEX32:
499         case GAL_TYPE_COMPLEX64:
500           error(EXIT_FAILURE, 0, "%s: copying to %s type not currently "
501                 "supported", __func__, gal_type_name(to->type, 1));
502           break;
503 
504         default:
505           error(EXIT_FAILURE, 0, "%s: type %d not recognized for to->type",
506                 __func__, to->type);
507         }
508 
509       /* Read/put the input into the output. */
510       if(to->type==GAL_TYPE_STRING)
511         gal_checkset_allocate_copy(strarr[i], &outstrarr[i]);
512       else
513         {
514           if( gal_type_from_string(&ptr, strarr[i], to->type) )
515             data_copy_to_string_not_parsed(strarr[i], ptr, to->type);
516         }
517     }
518 }
519 
520 
521 
522 
523 
524 /* Macros for copying to a string. */
525 #define COPY_TO_STR_INT(CTYPE, BLANK, FMT) {                            \
526     CTYPE *a=from->array;                                               \
527     for(i=0;i<from->size;++i)                                           \
528       {                                                                 \
529         if(a[i]!=BLANK)                                                 \
530           {                                                             \
531             if( asprintf(&strarr[i], FMT, a[i])<0 )                     \
532               error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__); \
533           }                                                             \
534         else                                                            \
535           gal_checkset_allocate_copy(GAL_BLANK_STRING, &strarr[i]);     \
536       }                                                                 \
537   }
538 
539 #define COPY_TO_STR_FLT(CTYPE, BLANK) {                                 \
540     CTYPE *a=from->array;                                               \
541     for(i=0;i<from->size;++i)                                           \
542       {                                                                 \
543         if(isnan(BLANK)) isblank = isnan(a[i]) ? 1 : 0;                 \
544         else             isblank = a[i]==BLANK ? 1 : 0;                 \
545         if(isblank==0)                                                  \
546           {                                                             \
547             if( asprintf(&strarr[i], "%f", a[i])<0 )                    \
548               error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__); \
549           }                                                             \
550         else gal_checkset_allocate_copy(GAL_BLANK_STRING, &strarr[i]);  \
551       }                                                                 \
552   }
553 
554 /* Convert any given type into a string by printing it into the elements of
555    the already allocated 'to->array'. */
556 static void
data_copy_to_string(gal_data_t * from,gal_data_t * to)557 data_copy_to_string(gal_data_t *from, gal_data_t *to)
558 {
559   size_t i;
560   int isblank;
561   char **strarr=to->array, **instrarr=from->array;
562 
563   /* Sanity check */
564   if(to->type!=GAL_TYPE_STRING)
565     error(EXIT_FAILURE, 0, "%s: 'to' must have a string type", __func__);
566   if(from->block)
567     error(EXIT_FAILURE, 0, "%s: tile inputs not currently supported "
568           "('block' element must be NULL). Please contact us at %s so we "
569           "can implement this feature", __func__, PACKAGE_BUGREPORT);
570 
571   /* Do the copying */
572   switch(from->type)
573     {
574     case GAL_TYPE_UINT8:
575       COPY_TO_STR_INT(uint8_t,  GAL_BLANK_UINT8,  "%"PRIu8);  break;
576 
577     case GAL_TYPE_INT8:
578       COPY_TO_STR_INT(int8_t,   GAL_BLANK_INT8,   "%"PRId8);  break;
579 
580     case GAL_TYPE_UINT16:
581       COPY_TO_STR_INT(uint16_t, GAL_BLANK_UINT16, "%"PRIu16); break;
582 
583     case GAL_TYPE_INT16:
584       COPY_TO_STR_INT(int16_t,  GAL_BLANK_INT16,  "%"PRId16); break;
585 
586     case GAL_TYPE_UINT32:
587       COPY_TO_STR_INT(uint32_t, GAL_BLANK_UINT32, "%"PRIu32); break;
588 
589     case GAL_TYPE_INT32:
590       COPY_TO_STR_INT(int32_t,  GAL_BLANK_INT32,  "%"PRId32); break;
591 
592     case GAL_TYPE_UINT64:
593       COPY_TO_STR_INT(uint64_t, GAL_BLANK_UINT64, "%"PRIu64); break;
594 
595     case GAL_TYPE_INT64:
596       COPY_TO_STR_INT(int64_t,  GAL_BLANK_INT64,  "%"PRId64); break;
597 
598     case GAL_TYPE_FLOAT32:
599       COPY_TO_STR_FLT(float, GAL_BLANK_FLOAT32);              break;
600 
601     case GAL_TYPE_FLOAT64:
602       COPY_TO_STR_FLT(double, GAL_BLANK_FLOAT32);             break;
603 
604     case GAL_TYPE_STRING:
605       for(i=0;i<from->size;++i)
606         gal_checkset_allocate_copy(instrarr[i], &strarr[i]);
607       break;
608 
609     case GAL_TYPE_BIT:
610     case GAL_TYPE_STRLL:
611     case GAL_TYPE_COMPLEX32:
612     case GAL_TYPE_COMPLEX64:
613       error(EXIT_FAILURE, 0, "%s: copying to %s type not currently supported",
614             __func__, gal_type_name(from->type, 1));
615       break;
616 
617     default:
618       error(EXIT_FAILURE, 0, "%s: type %d not recognized for 'from->type'",
619             __func__, from->type);
620     }
621 }
622 
623 
624 
625 
626 
627 #define COPY_OT_IT_SET(OT, IT) {                                        \
628     OT ob, *restrict o=out->array;                                      \
629     size_t increment=0, num_increment=1;                                \
630     size_t mclen=0, contig_len=in->dsize[in->ndim-1];                   \
631     IT ib, *ist=NULL, *restrict i=in->array, *f=i+in->size;             \
632     size_t s_e_ind[2]={0,iblock->size-1}; /* -1: this is INCLUSIVE */   \
633                                                                         \
634     /* If we are on a tile, the default values need to change. */       \
635     if(in!=iblock)                                                      \
636       ist=gal_tile_start_end_ind_inclusive(in, iblock, s_e_ind);        \
637                                                                         \
638     /* Constant preparations before the loop. */                        \
639     if(iblock->type==out->type)                                         \
640       mclen = in==iblock ? iblock->size : contig_len;                   \
641     else                                                                \
642       {                                                                 \
643         gal_blank_write(&ob, out->type);                                \
644         gal_blank_write(&ib, iblock->type);                             \
645       }                                                                 \
646                                                                         \
647     /* Parse over the input and copy it. */                             \
648     while( s_e_ind[0] + increment <= s_e_ind[1] )                       \
649       {                                                                 \
650         /* If we are on a tile, reset 'i' and  'f' for each round. */   \
651         if(in!=iblock)                                                  \
652           f = ( i = ist + increment ) + contig_len;                     \
653                                                                         \
654         /* When the types are the same just use memcopy, otherwise, */  \
655         /* We'll have to read each number (and use internal         */  \
656         /* conversion). */                                              \
657         if(iblock->type==out->type)                                     \
658           {                                                             \
659             memcpy(o, i, mclen*gal_type_sizeof(iblock->type));          \
660             o += mclen;                                                 \
661           }                                                             \
662         else                                                            \
663           {                                                             \
664             /* If the blank is a NaN value (only for floating point  */ \
665             /* types), it will fail any comparison, so we'll exploit */ \
666             /* this property in such cases. For other cases, a       */ \
667             /* '*i==ib' is enough.                                   */ \
668             if(ib==ib) do *o++ = *i==ib ? ob : *i; while(++i<f);        \
669             else       do *o++ = *i!=*i ? ob : *i; while(++i<f);        \
670           }                                                             \
671                                                                         \
672         /* Update the increment from the start of the input. */         \
673         increment += ( in==iblock ? iblock->size                        \
674                        : gal_tile_block_increment(iblock, in->dsize,    \
675                                                   num_increment++,      \
676                                                   NULL) );              \
677       }                                                                 \
678   }
679 
680 
681 
682 
683 
684 /* gal_data_copy_to_new_type: Output type is set, now choose the input
685    type. */
686 #define COPY_OT_SET(OT)                                                 \
687   switch(iblock->type)                                                  \
688     {                                                                   \
689     case GAL_TYPE_UINT8:      COPY_OT_IT_SET(OT, uint8_t  );    break;  \
690     case GAL_TYPE_INT8:       COPY_OT_IT_SET(OT, int8_t   );    break;  \
691     case GAL_TYPE_UINT16:     COPY_OT_IT_SET(OT, uint16_t );    break;  \
692     case GAL_TYPE_INT16:      COPY_OT_IT_SET(OT, int16_t  );    break;  \
693     case GAL_TYPE_UINT32:     COPY_OT_IT_SET(OT, uint32_t );    break;  \
694     case GAL_TYPE_INT32:      COPY_OT_IT_SET(OT, int32_t  );    break;  \
695     case GAL_TYPE_UINT64:     COPY_OT_IT_SET(OT, uint64_t );    break;  \
696     case GAL_TYPE_INT64:      COPY_OT_IT_SET(OT, int64_t  );    break;  \
697     case GAL_TYPE_FLOAT32:    COPY_OT_IT_SET(OT, float    );    break;  \
698     case GAL_TYPE_FLOAT64:    COPY_OT_IT_SET(OT, double   );    break;  \
699     case GAL_TYPE_STRING:     data_copy_from_string(in, out);   break;  \
700     case GAL_TYPE_BIT:                                                  \
701     case GAL_TYPE_STRLL:                                                \
702     case GAL_TYPE_COMPLEX32:                                            \
703     case GAL_TYPE_COMPLEX64:                                            \
704       error(EXIT_FAILURE, 0, "%s: copying from %s type to a numeric "   \
705             "(real) type not supported", "COPY_OT_SET",                 \
706             gal_type_name(in->type, 1));                                \
707       break;                                                            \
708                                                                         \
709     default:                                                            \
710       error(EXIT_FAILURE, 0, "%s: type code %d not recognized for "     \
711             "'in->type'", "COPY_OT_SET", in->type);                     \
712     }
713 
714 
715 
716 
717 
718 /* Wrapper for 'gal_data_copy_to_new_type', but will copy to the same type
719    as the input. Recall that if the input is a tile (a part of the input,
720    which is not-contiguous if it has more than one dimension), then the
721    output will have only the elements that cover the tile.*/
722 gal_data_t *
gal_data_copy(gal_data_t * in)723 gal_data_copy(gal_data_t *in)
724 {
725   return gal_data_copy_to_new_type(in, gal_tile_block(in)->type);
726 }
727 
728 
729 
730 
731 
732 /* Copy a given data structure to a new one with any type (for the
733    output). The input can be a tile, in which case the output will be a
734    contiguous patch of memory that has all the values within the input tile
735    in the requested type. */
736 gal_data_t *
gal_data_copy_to_new_type(gal_data_t * in,uint8_t newtype)737 gal_data_copy_to_new_type(gal_data_t *in, uint8_t newtype)
738 {
739   gal_data_t *out;
740 
741   /* Allocate the output datastructure. */
742   out=gal_data_alloc(NULL, newtype, in->ndim, in->dsize, in->wcs,
743                      0, in->minmapsize, in->quietmmap, in->name,
744                      in->unit, in->comment);
745 
746   /* Fill in the output array: */
747   gal_data_copy_to_allocated(in, out);
748 
749   /* Return the created array */
750   return out;
751 }
752 
753 
754 
755 
756 
757 /* Copy the input data structure into a new type and free the allocated
758    space. */
759 gal_data_t *
gal_data_copy_to_new_type_free(gal_data_t * in,uint8_t newtype)760 gal_data_copy_to_new_type_free(gal_data_t *in, uint8_t newtype)
761 {
762   gal_data_t *out, *iblock=gal_tile_block(in);
763 
764   /* In a general application, it might happen that the type is equal with
765      the type of the input and the input isn't a tile. Since the job of
766      this function is to free the input dataset, and the user just wants
767      one dataset after this function finishes, we can safely just return
768      the input. */
769   if(newtype==iblock->type && in==iblock)
770     return in;
771   else
772     {
773       out=gal_data_copy_to_new_type(in, newtype);
774       if(iblock==in)
775         gal_data_free(in);
776       else
777         fprintf(stderr, "#####\nWarning from "
778                 "'gal_data_copy_to_new_type_free'\n#####\n The input "
779                 "dataset is a tile, not a contiguous (fully allocated) "
780                 "patch of memory. So it has not been freed. Please use "
781                 "'gal_data_copy_to_new_type' to avoid this warning.\n"
782                 "#####");
783       return out;
784     }
785 }
786 
787 
788 
789 
790 
791 /* Copy a given dataset ('in') into an already allocated dataset 'out' (the
792    actual dataset and its 'array' element). The meta-data of 'in' (except
793    for 'block') will be fully copied into 'out' also. 'out->size' will be
794    used to find the available space in the allocated space.
795 
796    When 'in->size != out->size' this function will behave as follows:
797 
798       'out->size < in->size': it won't re-allocate the necessary space, it
799           will abort with an error, so please check before calling this
800           function.
801 
802       'out->size > in->size': it will update 'out->size' and 'out->dsize'
803           to be the same as the input. So if you want to re-use a
804           pre-allocated space with varying input sizes, be sure to reset
805           'out->size' before every call to this function. */
806 void
gal_data_copy_to_allocated(gal_data_t * in,gal_data_t * out)807 gal_data_copy_to_allocated(gal_data_t *in, gal_data_t *out)
808 {
809   gal_data_t *iblock=gal_tile_block(in);
810 
811   /* Make sure the number of allocated elements (of whatever type) in the
812      output is not smaller than the input. Note that the type is irrelevant
813      because we will be doing type conversion if they differ.*/
814   if( out->size < in->size  )
815     error(EXIT_FAILURE, 0, "%s: the output dataset must be equal or larger "
816           "than the input. the sizes are %zu and %zu respectively", __func__,
817           out->size, in->size);
818   if( out->ndim != in->ndim )
819     error(EXIT_FAILURE, 0, "%s: the output dataset must have the same number "
820           "of dimensions, the dimensions are %zu and %zu respectively",
821           __func__, out->ndim, in->ndim);
822 
823   /* Free possibly allocated meta-data strings. */
824   if(out->name)    free(out->name);
825   if(out->unit)    free(out->unit);
826   if(out->comment) free(out->comment);
827 
828   /* Write the basic meta-data. */
829   out->flag           = in->flag;
830   out->next           = in->next;
831   out->status         = in->status;
832   out->disp_width     = in->disp_width;
833   out->disp_precision = in->disp_precision;
834   gal_checkset_allocate_copy(in->name,    &out->name);
835   gal_checkset_allocate_copy(in->unit,    &out->unit);
836   gal_checkset_allocate_copy(in->comment, &out->comment);
837 
838   /* Do the copying. */
839   if(in->array)
840     switch(out->type)
841       {
842       case GAL_TYPE_UINT8:   COPY_OT_SET( uint8_t  );      break;
843       case GAL_TYPE_INT8:    COPY_OT_SET( int8_t   );      break;
844       case GAL_TYPE_UINT16:  COPY_OT_SET( uint16_t );      break;
845       case GAL_TYPE_INT16:   COPY_OT_SET( int16_t  );      break;
846       case GAL_TYPE_UINT32:  COPY_OT_SET( uint32_t );      break;
847       case GAL_TYPE_INT32:   COPY_OT_SET( int32_t  );      break;
848       case GAL_TYPE_UINT64:  COPY_OT_SET( uint64_t );      break;
849       case GAL_TYPE_INT64:   COPY_OT_SET( int64_t  );      break;
850       case GAL_TYPE_FLOAT32: COPY_OT_SET( float    );      break;
851       case GAL_TYPE_FLOAT64: COPY_OT_SET( double   );      break;
852       case GAL_TYPE_STRING:  data_copy_to_string(in, out); break;
853 
854       case GAL_TYPE_BIT:
855       case GAL_TYPE_STRLL:
856       case GAL_TYPE_COMPLEX32:
857       case GAL_TYPE_COMPLEX64:
858         error(EXIT_FAILURE, 0, "%s: copying to %s type not yet supported",
859               __func__, gal_type_name(out->type, 1));
860         break;
861 
862       default:
863         error(EXIT_FAILURE, 0, "%s: type %d not recognized for 'out->type'",
864               __func__, out->type);
865       }
866   else out->array=NULL;
867 
868   /* Correct the sizes of the output to be the same as the input. If it is
869      equal, there is no problem, if not, the size information will be
870      changed, so if you want to use this allocated space again, be sure to
871      re-set the size parameters. */
872   out->size=in->size;
873   memcpy(out->dsize, in->dsize, in->ndim * sizeof *(in->dsize) );
874 }
875 
876 
877 
878 
879 
880 /* Just a wrapper around 'gal_type_from_string_auto', to return a
881    'gal_data_t' dataset hosting the allocated number. */
882 gal_data_t *
gal_data_copy_string_to_number(char * string)883 gal_data_copy_string_to_number(char *string)
884 {
885   void *ptr;
886   uint8_t type;
887   size_t dsize=1;
888   ptr=gal_type_string_to_number(string, &type);
889   return ( ptr
890            ? gal_data_alloc(ptr, type, 1, &dsize, NULL, 0, -1,
891                             1, NULL, NULL, NULL)
892            : NULL );
893 }
894