1 /*********************************************************************
2 Type -- Type information and basic operations.
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 <stdio.h>
26 #include <errno.h>
27 #include <error.h>
28 #include <float.h>
29 #include <ctype.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <inttypes.h>
33
34 #include <gnuastro/type.h>
35 #include <gnuastro/data.h>
36 #include <gnuastro/list.h>
37 #include <gnuastro/pointer.h>
38 #include <gnuastro-internal/checkset.h>
39
40
41
42
43
44
45 /*************************************************************
46 ************** General info ***************
47 *************************************************************/
48 size_t
gal_type_sizeof(uint8_t type)49 gal_type_sizeof(uint8_t type)
50 {
51 /* Allocate space for the array to keep the image. */
52 switch(type)
53 {
54 case GAL_TYPE_BIT:
55 error(EXIT_FAILURE, 0, "%s: bit types are not currently supported, "
56 "please get in touch with us to implement it", __func__);
57
58 /* The parenthesis after sizeof is not a function, it is actually a
59 type cast, so we have put a space between size of and the
60 parenthesis to highlight this. In C, 'sizeof' is an operator, not
61 a function.*/
62 case GAL_TYPE_UINT8: return sizeof (uint8_t);
63 case GAL_TYPE_INT8: return sizeof (int8_t);
64 case GAL_TYPE_UINT16: return sizeof (uint16_t);
65 case GAL_TYPE_INT16: return sizeof (int16_t);
66 case GAL_TYPE_UINT32: return sizeof (uint32_t);
67 case GAL_TYPE_INT32: return sizeof (int32_t);
68 case GAL_TYPE_UINT64: return sizeof (uint64_t);
69 case GAL_TYPE_INT64: return sizeof (int64_t);
70
71 case GAL_TYPE_FLOAT32:
72 if( sizeof (float) != 4 )
73 error(EXIT_FAILURE, 0, "%s: 'float' is not 32 bits on this machine",
74 __func__);
75 return sizeof (float);
76
77 case GAL_TYPE_FLOAT64:
78 if( sizeof (double) != 8 )
79 error(EXIT_FAILURE, 0, "%s: 'double' is not 64 bits on this machine",
80 __func__);
81 return sizeof (double);
82
83 case GAL_TYPE_COMPLEX32:
84 if( sizeof (float) != 4 )
85 error(EXIT_FAILURE, 0, "%s: 'float' is not 32 bits on this machine",
86 __func__);
87 return sizeof (gsl_complex_float);
88
89 case GAL_TYPE_COMPLEX64:
90 if( sizeof (double) != 8 )
91 error(EXIT_FAILURE, 0, "%s: 'double' is not 64 bits on this machine",
92 __func__);
93 return sizeof (gsl_complex);
94
95 case GAL_TYPE_STRING:
96 return sizeof (char *);
97
98 default:
99 error(EXIT_FAILURE, 0, "%s: type value of %d not recognized",
100 __func__, type);
101 }
102
103 error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s so we can find "
104 "the cause of the problem. Control should not have reached the end of "
105 "this function", __func__, PACKAGE_BUGREPORT);
106 return -1;
107 }
108
109
110
111
112
113 char *
gal_type_name(uint8_t type,int long_name)114 gal_type_name(uint8_t type, int long_name)
115 {
116 switch(type)
117 {
118 case GAL_TYPE_BIT:
119 if(long_name) return "bit"; else return "b";
120
121 case GAL_TYPE_UINT8:
122 if(long_name) return "uint8"; else return "u8";
123
124 case GAL_TYPE_INT8:
125 if(long_name) return "int8"; else return "i8";
126
127 case GAL_TYPE_UINT16:
128 if(long_name) return "uint16"; else return "u16";
129
130 case GAL_TYPE_INT16:
131 if(long_name) return "int16"; else return "i16";
132
133 case GAL_TYPE_UINT32:
134 if(long_name) return "uint32"; else return "u32";
135
136 case GAL_TYPE_INT32:
137 if(long_name) return "int32"; else return "i32";
138
139 case GAL_TYPE_UINT64:
140 if(long_name) return "uint64"; else return "u64";
141
142 case GAL_TYPE_INT64:
143 if(long_name) return "int64"; else return "i64";
144
145 case GAL_TYPE_FLOAT32:
146 if(long_name) return "float32"; else return "f32";
147
148 case GAL_TYPE_FLOAT64:
149 if(long_name) return "float64"; else return "f64";
150
151 case GAL_TYPE_COMPLEX32:
152 if(long_name) return "complex32"; else return "c32";
153
154 case GAL_TYPE_COMPLEX64:
155 if(long_name) return "complex64"; else return "c64";
156
157 case GAL_TYPE_STRING:
158 if(long_name) return "string"; else return "str";
159
160 case GAL_TYPE_STRLL:
161 if(long_name) return "string linked list"; else return "strll";
162
163 default:
164 error(EXIT_FAILURE, 0, "%s: type value of %d not recognized",
165 __func__, type);
166 }
167
168 /* Any of the cases above should return this function, so if control
169 reaches here, there is a bug. */
170 error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s so we can "
171 "address the problem. Control should not have reached the end of "
172 "this function", __func__, PACKAGE_BUGREPORT);
173 return NULL;
174 }
175
176
177
178
179
180 uint8_t
gal_type_from_name(char * str)181 gal_type_from_name(char *str)
182 {
183 if( !strcmp(str, "b") || !strcmp(str, "bit") )
184 return GAL_TYPE_BIT;
185
186 else if( !strcmp(str, "u8") || !strcmp(str, "uint8") )
187 return GAL_TYPE_UINT8;
188
189 else if( !strcmp(str, "i8") || !strcmp(str, "int8") )
190 return GAL_TYPE_INT8;
191
192 else if( !strcmp(str, "u16") || !strcmp(str, "uint16") )
193 return GAL_TYPE_UINT16;
194
195 else if( !strcmp(str, "i16") || !strcmp(str, "int16") )
196 return GAL_TYPE_INT16;
197
198 else if( !strcmp(str, "u32") || !strcmp(str, "uint32") )
199 return GAL_TYPE_UINT32;
200
201 else if( !strcmp(str, "i32") || !strcmp(str, "int32") )
202 return GAL_TYPE_INT32;
203
204 else if( !strcmp(str, "u64") || !strcmp(str, "uint64") )
205 return GAL_TYPE_UINT64;
206
207 else if( !strcmp(str, "i64") || !strcmp(str, "int64") )
208 return GAL_TYPE_INT64;
209
210 else if( !strcmp(str, "f32") || !strcmp(str, "float32") )
211 return GAL_TYPE_FLOAT32;
212
213 else if( !strcmp(str, "f64") || !strcmp(str, "float64") )
214 return GAL_TYPE_FLOAT64;
215
216 else if( !strcmp(str, "c32") || !strcmp(str, "complex32") )
217 return GAL_TYPE_COMPLEX32;
218
219 else if( !strcmp(str, "c64") || !strcmp(str, "complex64") )
220 return GAL_TYPE_COMPLEX64;
221
222 else if( !strcmp(str, "str") || !strcmp(str, "string") )
223 return GAL_TYPE_STRING;
224
225 else
226 return GAL_TYPE_INVALID;
227
228 /* Any of the cases above should return this function, so if control
229 reaches here, there is a bug. */
230 error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s so we can "
231 "address the problem. Control must not have reached the end of this "
232 "function", __func__, PACKAGE_BUGREPORT);
233 return 0;
234 }
235
236
237
238
239
240 /* Put the minimum (or maximum for the 'gal_data_type_max') value for the
241 type in the space (that must already be allocated before the call to
242 this function) pointed to by in. */
243 void
gal_type_min(uint8_t type,void * in)244 gal_type_min(uint8_t type, void *in)
245 {
246 switch(type)
247 {
248 case GAL_TYPE_UINT8: *(uint8_t *) in = 0; break;
249 case GAL_TYPE_INT8: *(int8_t *) in = INT8_MIN; break;
250 case GAL_TYPE_UINT16: *(uint16_t *) in = 0; break;
251 case GAL_TYPE_INT16: *(int16_t *) in = INT16_MIN; break;
252 case GAL_TYPE_UINT32: *(uint32_t *) in = 0; break;
253 case GAL_TYPE_INT32: *(int32_t *) in = INT32_MIN; break;
254 case GAL_TYPE_UINT64: *(uint64_t *) in = 0; break;
255 case GAL_TYPE_INT64: *(int64_t *) in = INT64_MIN; break;
256 case GAL_TYPE_FLOAT32: *(float *) in = -FLT_MAX; break;
257 case GAL_TYPE_FLOAT64: *(double *) in = -DBL_MAX; break;
258 default:
259 error(EXIT_FAILURE, 0, "%s: type code %d not recognized", __func__, type);
260 }
261 }
262
263
264
265
266
267 void
gal_type_max(uint8_t type,void * in)268 gal_type_max(uint8_t type, void *in)
269 {
270 switch(type)
271 {
272 case GAL_TYPE_UINT8: *(uint8_t *) in = UINT8_MAX; break;
273 case GAL_TYPE_INT8: *(int8_t *) in = INT8_MAX; break;
274 case GAL_TYPE_UINT16: *(uint16_t *) in = UINT16_MAX; break;
275 case GAL_TYPE_INT16: *(int16_t *) in = INT16_MAX; break;
276 case GAL_TYPE_UINT32: *(uint32_t *) in = UINT32_MAX; break;
277 case GAL_TYPE_INT32: *(int32_t *) in = INT32_MAX; break;
278 case GAL_TYPE_UINT64: *(uint64_t *) in = UINT64_MAX; break;
279 case GAL_TYPE_INT64: *(int64_t *) in = INT64_MAX; break;
280 case GAL_TYPE_FLOAT32: *(float *) in = FLT_MAX; break;
281 case GAL_TYPE_FLOAT64: *(double *) in = DBL_MAX; break;
282 default:
283 error(EXIT_FAILURE, 0, "%s: type code %d not recognized", __func__,
284 type);
285 }
286 }
287
288
289
290
291
292 int
gal_type_is_int(uint8_t type)293 gal_type_is_int(uint8_t type)
294 {
295 switch(type)
296 {
297 case GAL_TYPE_UINT8: return 1;
298 case GAL_TYPE_INT8: return 1;
299 case GAL_TYPE_UINT16: return 1;
300 case GAL_TYPE_INT16: return 1;
301 case GAL_TYPE_UINT32: return 1;
302 case GAL_TYPE_INT32: return 1;
303 case GAL_TYPE_UINT64: return 1;
304 case GAL_TYPE_INT64: return 1;
305 default: return 0;
306 }
307 }
308
309
310
311
312
313 /* Since linked lists need a different process than arrays, for functions
314 that work on both, it is convenient to simiplify the check with this
315 function. */
316 int
gal_type_is_list(uint8_t type)317 gal_type_is_list(uint8_t type)
318 {
319 return type==GAL_TYPE_STRLL;
320 }
321
322
323
324
325
326 int
gal_type_out(int first_type,int second_type)327 gal_type_out(int first_type, int second_type)
328 {
329 return first_type > second_type ? first_type : second_type;
330 }
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351 /*************************************************************
352 ************** To/from string ***************
353 *************************************************************/
354 /* Write the bit (0 or 1) contents of 'in' into a string ready for
355 printing. 'size' is used to determine the number of bytes to print. The
356 output string will be dynamically allocated within this function. This
357 can be useful for easy checking of bit flag values, for example in an
358 expression like below:
359
360 printf("flag: %s\n", gal_type_bit_string(&flag, sizeof flag) ); */
361 char *
gal_type_bit_string(void * in,size_t size)362 gal_type_bit_string(void *in, size_t size)
363 {
364 size_t i;
365 char *byte=in;
366 char *str=gal_pointer_allocate(GAL_TYPE_UINT8, 8*size+1, 0, __func__,
367 "str");
368
369 /* Print the bits into the allocated string. This was inspired from
370
371 http://stackoverflow.com/questions/111928/is-there-a-printf-converter-to-print-in-binary-format */
372 for(i=0;i<size;++i)
373 sprintf(str+i*8, "%c%c%c%c%c%c%c%c ",
374 (byte[i] & 0x80 ? '1' : '0'), (byte[i] & 0x40 ? '1' : '0'),
375 (byte[i] & 0x20 ? '1' : '0'), (byte[i] & 0x10 ? '1' : '0'),
376 (byte[i] & 0x08 ? '1' : '0'), (byte[i] & 0x04 ? '1' : '0'),
377 (byte[i] & 0x02 ? '1' : '0'), (byte[i] & 0x01 ? '1' : '0') );
378
379 /* Return the allocated and filled string. */
380 return str;
381 }
382
383
384
385
386
387 /* Write the contents of memory that 'ptr' points to as a string of type
388 'type'.*/
389 #define TO_STRING(CTYPE, FMT) { \
390 if( asprintf(&str, FMT, *(CTYPE *)ptr)<0 ) \
391 error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__); }
392
393 char *
gal_type_to_string(void * ptr,uint8_t type,int quote_if_str_has_space)394 gal_type_to_string(void *ptr, uint8_t type, int quote_if_str_has_space)
395 {
396 char *c, *str=NULL;
397 switch(type)
398 {
399 /* For a string we might need to make sure it has no white space
400 characters, if it does, it can be printed it within quotation
401 signs. */
402 case GAL_TYPE_STRING:
403 if(quote_if_str_has_space)
404 {
405 c=*(char **)ptr; while(*c!='\0') if(isspace(*c++)) break;
406 if(*c=='\0')
407 {
408 if( asprintf(&str, "%s", *(char **)ptr)<0 )
409 error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
410 }
411 else
412 {
413 if( asprintf(&str, "\"%s\" ", *(char **)ptr)<0 )
414 error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
415 }
416 }
417 else
418 if( asprintf(&str, "%s", *(char **)ptr)<0 )
419 error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
420 break;
421
422 case GAL_TYPE_UINT8: TO_STRING( uint8_t, "%"PRIu8 ); break;
423 case GAL_TYPE_INT8: TO_STRING( int8_t, "%"PRId8 ); break;
424 case GAL_TYPE_UINT16: TO_STRING( uint16_t, "%"PRIu16 ); break;
425 case GAL_TYPE_INT16: TO_STRING( int16_t, "%"PRId16 ); break;
426 case GAL_TYPE_UINT32: TO_STRING( uint32_t, "%"PRIu32 ); break;
427 case GAL_TYPE_INT32: TO_STRING( int32_t, "%"PRId32 ); break;
428 case GAL_TYPE_UINT64: TO_STRING( uint64_t, "%"PRIu64 ); break;
429 case GAL_TYPE_INT64: TO_STRING( int64_t, "%"PRId64 ); break;
430 case GAL_TYPE_FLOAT32: TO_STRING( float, "%.6g" ); break;
431 case GAL_TYPE_FLOAT64: TO_STRING( double, "%.10g" ); break;
432
433 default:
434 error(EXIT_FAILURE, 0, "%s: type code %d not recognized",
435 __func__, type);
436 }
437 return str;
438 }
439
440
441
442
443
444 /* Read a string as a given data type and put a the pointer to it in
445 *out. When the input '*out!=NULL', then it is assumed to be allocated
446 and the value will be simply put there. If '*out==NULL', then space will
447 be allocated for the given type and the string's value (in the given
448 type) will be stored there.
449
450 Note that when we are dealing with a string type, '*out' should be
451 interpretted as 'char **' (one element in an array of pointers to
452 different strings). In other words, 'out' should be 'char ***'.
453
454 This function can be used to fill in arrays of numbers from strings (in
455 an already allocated data structure), or add nodes to a linked list. For
456 an array, you have to pass the pointer to the 'i'th element where you
457 want the value to be stored, for example &(array[i]).
458
459 If parsing was successful, it will return a 0. If there was a problem,
460 it will return 1. */
461 int
gal_type_from_string(void ** out,char * string,uint8_t type)462 gal_type_from_string(void **out, char *string, uint8_t type)
463 {
464 long l;
465 double d;
466 void *value;
467 char *tailptr;
468 int status=0, allocated=0;
469
470 /* If the output is NULL, then allocate the necessary space if we are not
471 dealing with a linked list. In a linked list, a NULL value is
472 meaningful (it is the end of the list). */
473 if( *out==NULL && !gal_type_is_list(type) )
474 {
475 allocated=1;
476 *out=gal_pointer_allocate(type, 1, 0, __func__, "out");
477 }
478 value=*out;
479
480 /* Read the string depending on the type. */
481 switch(type)
482 {
483
484 /* Linked lists, currently only string linked lists. */
485 case GAL_TYPE_STRLL:
486 gal_list_str_add( (struct gal_list_str_t **)out, string, 1);
487 break;
488
489 /* String, just allocate and copy the string and keep its pointer in
490 the place '*out' points to (for strings, '*out' is 'char **'). */
491 case GAL_TYPE_STRING:
492 gal_checkset_allocate_copy(string, value);
493 break;
494
495 /* Floating point: Read it as a double or long, then put it in the
496 array. When the conversion can't be done (the string isn't a number
497 for example), then just assume no blank value was given. */
498 case GAL_TYPE_FLOAT32:
499 case GAL_TYPE_FLOAT64:
500 d=strtod(string, &tailptr);
501 if(*tailptr!='\0')
502 status=1;
503 else
504 {
505 if(type==GAL_TYPE_FLOAT32) *(float *) value=d;
506 else *(double *) value=d;
507 }
508 break;
509
510 /* Integers. */
511 default:
512 l=strtol(string, &tailptr, 0);
513 if(*tailptr!='\0')
514 status=1;
515 else
516 switch(type)
517 {
518 /* The signed values can easily be put in. */
519 case GAL_TYPE_INT8: *(int8_t *) value = l; break;
520 case GAL_TYPE_INT16: *(int16_t *) value = l; break;
521 case GAL_TYPE_INT32: *(int32_t *) value = l; break;
522 case GAL_TYPE_INT64: *(int64_t *) value = l; break;
523
524 /* For the unsigned types, the value has to be positive, so if
525 the input was negative, then just return a status of one and
526 don't store the value. */
527 default:
528 if(l<0)
529 status=1;
530 else
531 switch(type)
532 {
533 case GAL_TYPE_UINT8: *(uint8_t *) value=l; break;
534 case GAL_TYPE_UINT16: *(uint16_t *) value=l; break;
535 case GAL_TYPE_UINT32: *(uint32_t *) value=l; break;
536 case GAL_TYPE_UINT64: *(uint64_t *) value=l; break;
537 default:
538 error(EXIT_FAILURE, 0, "%s: type code %d not recognized",
539 __func__, type);
540 }
541 }
542 }
543
544 /* If reading was unsuccessful, then free the space if it was allocated,
545 then return the status, don't touch the pointer. */
546 if(status && allocated)
547 {
548 free(*out);
549 *out=NULL;
550 }
551 return status;
552 }
553
554
555
556
557
558 /* If the data structure was correctly created (the string was a number),
559 then return its pointer. Otherwise, return NULL. */
560 void *
gal_type_string_to_number(char * string,uint8_t * type)561 gal_type_string_to_number(char *string, uint8_t *type)
562 {
563 void *ptr, *out;
564 int fnz=-1, lnz=0; /* 'F'irst (or 'L'ast) 'N'on-'Z'ero. */
565 uint8_t forcedfloat=0;
566 char *c, *tailptr, *cp;
567
568 /* Define initial spaces to keep the value. */
569 uint8_t u8; int8_t i8; uint16_t u16; int16_t i16;
570 uint32_t u32; int32_t i32; uint64_t u64; int64_t i64;
571 float f; double d;
572
573 /* First see if the number is a double (the most generic). */
574 d=strtod(string, &tailptr);
575 if(*tailptr=='f') { if(tailptr[1]=='\0') forcedfloat=1; else return NULL; }
576 else if (*tailptr!='\0') return NULL;
577
578 /* The number has been parsed successfully as a number. But if it
579 contains a '.', then it must a "forced" float also. This won't be a
580 problem in scenarios like '.2', but people may use '2.' or '2.0' to
581 force a float and this loop is necessary in such cases. */
582 for(c=string; *c!='\0'; ++c) if(*c=='.') { forcedfloat=1; break; }
583
584 /* See if the number is actually an integer: */
585 if( forcedfloat==0 && ceil(d) == d )
586 {
587 /* If the number is negative, put it in the signed types (based on
588 its value). If its zero or positive, then put it in the unsigned
589 types. */
590 if( d < 0 )
591 {
592 if (d>INT8_MIN) { i8=d; ptr=&i8; *type=GAL_TYPE_INT8; }
593 else if(d>INT16_MIN) { i16=d; ptr=&i16; *type=GAL_TYPE_INT16; }
594 else if(d>INT32_MIN) { i32=d; ptr=&i32; *type=GAL_TYPE_INT32; }
595 else { i64=d; ptr=&i64; *type=GAL_TYPE_INT64; }
596 }
597 else
598 {
599 /* Note that the blank values are set to the maximum values in
600 unsigned types. A blank value should be given as a blank
601 string to this function ('GAL_BLANK_STRING'). So, to avoid
602 confusing situations (for example when the user gives 255), if
603 the value is equal to the given maximum of the given type,
604 we'll assign it to a larger type. In other words, we won't be
605 using the '<=MAX', but '<MAX'. */
606 if (d<UINT8_MAX) { u8=d; ptr=&u8; *type=GAL_TYPE_UINT8; }
607 else if(d<UINT16_MAX) { u16=d; ptr=&u16; *type=GAL_TYPE_UINT16; }
608 else if(d<UINT32_MAX) { u32=d; ptr=&u32; *type=GAL_TYPE_UINT32; }
609 else { u64=d; ptr=&u64; *type=GAL_TYPE_UINT64; }
610 }
611 }
612 else
613 {
614 /* The maximum number of decimal digits to store in float or double
615 precision floating point are:
616
617 float: 23 mantissa bits + 1 hidden bit: log(224)÷log(10) = 7.22
618 double: 52 mantissa bits + 1 hidden bit: log(253)÷log(10) = 15.95
619
620 FLT_DIG (at least 6 in ISO C) keeps the number of digits (not zero
621 before or after) that can be represented by a single precision
622 floating point number. If there are more digits, then we should
623 store the value as a double precision.
624
625 Note that the number can have non-digit characters that we don't
626 want, like: '.', 'e', 'E', ','. */
627 for(cp=string;*cp!='\0';++cp)
628 if(isdigit(*cp) && *cp!='0' && fnz==-1)
629 fnz=cp-string;
630
631 /* In the previous loop, we went to the end of the string, so 'cp'
632 now points to its '\0'. We just have to iterate backwards! */
633 for(;cp!=string;--cp)
634 if(isdigit(*cp) && *cp!='0')
635 {
636 lnz=cp-string;
637 break;
638 }
639
640 /* Calculate the number of decimal digits and decide if it the number
641 should be a float or a double. */
642 if( lnz-fnz < FLT_DIG || ( d<FLT_MAX && d>FLT_MIN ) )
643 { f=d; ptr=&f; *type=GAL_TYPE_FLOAT32; }
644 else
645 { ptr=&d; *type=GAL_TYPE_FLOAT64; }
646 }
647
648 /* Allocate a one-element dataset, then copy the number into it. */
649 out=gal_pointer_allocate(*type, 1, 0, __func__, "out");
650 memcpy(out, ptr, gal_type_sizeof(*type));
651 return out;
652 }
653