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