1 /*
2  * Copyright 2020 University Corporation for Atmospheric Research
3  *
4  * This file is part of the UDUNITS-2 package.  See the file COPYRIGHT
5  * in the top-level source-directory of the package for copying and
6  * redistribution conditions.
7  */
8 /*
9  * Value converters for the udunits(3) library.
10  */
11 
12 /*LINTLIBRARY*/
13 
14 #include "config.h"
15 
16 #include "udunits2.h" // Accommodates Windows & includes "converter.h"
17 
18 #include <math.h>
19 #include <stddef.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 typedef struct {
25     cv_converter*	(*clone)(cv_converter*);
26     double		(*convertDouble)
27 	(const cv_converter*, double);
28     float*		(*convertFloats)
29 	(const cv_converter*, const float*, size_t, float*);
30     double*		(*convertDoubles)
31 	(const cv_converter*, const double*, size_t, double*);
32     int			(*getExpression)
33 	(const cv_converter*, char* buf, size_t, const char*);
34     void		(*free)(cv_converter*);
35 } ConverterOps;
36 
37 typedef struct {
38     ConverterOps*	ops;
39 } ReciprocalConverter;
40 
41 typedef struct {
42     ConverterOps*	ops;
43     double		value;
44 } ScaleConverter;
45 
46 typedef struct {
47     ConverterOps*	ops;
48     double		value;
49 } OffsetConverter;
50 
51 typedef struct {
52     ConverterOps*	ops;
53     double		slope;
54     double		intercept;
55 } GalileanConverter;
56 
57 typedef struct {
58     ConverterOps*	ops;
59     double		logE;
60 } LogConverter;
61 
62 typedef struct {
63     ConverterOps*	ops;
64     double		base;
65 } ExpConverter;
66 
67 typedef struct {
68     ConverterOps*	ops;
69     cv_converter*	first;
70     cv_converter*	second;
71 } CompositeConverter;
72 
73 union cv_converter {
74     ConverterOps*	ops;
75     ScaleConverter	scale;
76     OffsetConverter	offset;
77     GalileanConverter	galilean;
78     LogConverter	log;
79     ExpConverter	exp;
80     CompositeConverter	composite;
81 };
82 
83 #define CV_CLONE(conv)		((conv)->ops->clone(conv))
84 
85 #define IS_TRIVIAL(conv)	((conv)->ops == &trivialOps)
86 #define IS_RECIPROCAL(conv)	((conv)->ops == &reciprocalOps)
87 #define IS_SCALE(conv)		((conv)->ops == &scaleOps)
88 #define IS_OFFSET(conv)		((conv)->ops == &offsetOps)
89 #define IS_GALILEAN(conv)	((conv)->ops == &galileanOps)
90 #define IS_LOG(conv)		((conv)->ops == &logOps)
91 
92 
93 static void
nonFree(cv_converter * const conv)94 nonFree(
95     cv_converter* const conv)
96 {
97 }
98 
99 
100 static void
cvSimpleFree(cv_converter * const conv)101 cvSimpleFree(
102     cv_converter* const conv)
103 {
104     free(conv);
105 }
106 
107 
108 static int
cvNeedsParentheses(const char * const string)109 cvNeedsParentheses(
110     const char* const	string)
111 {
112     return strpbrk(string, " \t") != NULL &&
113 	(string[0] != '(' || string[strlen(string)-1] != ')');
114 }
115 
116 
117 /*******************************************************************************
118  * Trivial Converter:
119  ******************************************************************************/
120 
121 static cv_converter*
trivialClone(cv_converter * const conv)122 trivialClone(
123     cv_converter* const	conv)
124 {
125     return cv_get_trivial();
126 }
127 
128 
129 static double
trivialConvertDouble(const cv_converter * const conv,const double value)130 trivialConvertDouble(
131     const cv_converter* const	conv,
132     const double		value)
133 {
134     return value;
135 }
136 
137 
138 static float*
trivialConvertFloats(const cv_converter * const conv,const float * const in,const size_t count,float * out)139 trivialConvertFloats(
140     const cv_converter* const	conv,
141     const float* const		in,
142     const size_t		count,
143     float* 			out)
144 {
145     if (in == NULL || out == NULL) {
146 	out = NULL;
147     }
148     else {
149 	(void)memmove(out, in, count*sizeof(float));
150     }
151 
152     return out;
153 }
154 
155 
156 static double*
trivialConvertDoubles(const cv_converter * const conv,const double * const in,const size_t count,double * out)157 trivialConvertDoubles(
158     const cv_converter* const	conv,
159     const double* const		in,
160     const size_t		count,
161     double* 			out)
162 {
163     if (in == NULL || out == NULL) {
164 	out = NULL;
165     }
166     else {
167 	(void)memmove(out, in, count*sizeof(double));
168     }
169 
170     return out;
171 }
172 
173 
174 static int
trivialGetExpression(const cv_converter * const conv,char * const buf,const size_t max,const char * const variable)175 trivialGetExpression(
176     const cv_converter* const	conv,
177     char* const			buf,
178     const size_t		max,
179     const char* const		variable)
180 {
181     return snprintf(buf, max, "%s", variable);
182 }
183 
184 
185 static ConverterOps	trivialOps = {
186     trivialClone,
187     trivialConvertDouble,
188     trivialConvertFloats,
189     trivialConvertDoubles,
190     trivialGetExpression,
191     nonFree};
192 
193 static cv_converter	trivialConverter = {&trivialOps};
194 
195 
196 /*******************************************************************************
197  * Reciprocal Converter:
198  ******************************************************************************/
199 
200 static cv_converter*
reciprocalClone(cv_converter * const conv)201 reciprocalClone(
202     cv_converter* const	conv)
203 {
204     return cv_get_inverse();
205 }
206 
207 static double
reciprocalConvertDouble(const cv_converter * const conv,const double value)208 reciprocalConvertDouble(
209     const cv_converter* const	conv,
210     const double		value)
211 {
212     return 1.0 / value;
213 }
214 
215 
216 static float*
reciprocalConvertFloats(const cv_converter * const conv,const float * const in,const size_t count,float * out)217 reciprocalConvertFloats(
218     const cv_converter* const	conv,
219     const float* const		in,
220     const size_t		count,
221     float* 			out)
222 {
223     if (in == NULL || out == NULL) {
224 	out = NULL;
225     }
226     else {
227 	size_t	i;
228 
229 	if (in < out) {
230 	    for (i = count; i-- > 0;)
231 		out[i] = (float)(1.0f / in[i]);
232 	}
233 	else {
234 	    for (i = 0; i < count; i++)
235 		out[i] = (float)(1.0f / in[i]);
236 	}
237     }
238 
239     return out;
240 }
241 
242 
243 static double*
reciprocalConvertDoubles(const cv_converter * const conv,const double * const in,const size_t count,double * out)244 reciprocalConvertDoubles(
245     const cv_converter* const	conv,
246     const double* const		in,
247     const size_t		count,
248     double* 			out)
249 {
250     if (in == NULL || out == NULL) {
251 	out = NULL;
252     }
253     else {
254 	size_t	i;
255 
256 	if (in < out) {
257 	    for (i = count; i-- > 0;)
258 		out[i] = 1.0 / in[i];
259 	}
260 	else {
261 	    for (i = 0; i < count; i++)
262 		out[i] = 1.0 / in[i];
263 	}
264     }
265 
266     return out;
267 }
268 
269 
270 static int
reciprocalGetExpression(const cv_converter * const conv,char * const buf,const size_t max,const char * const variable)271 reciprocalGetExpression(
272     const cv_converter* const	conv,
273     char* const			buf,
274     const size_t		max,
275     const char* const		variable)
276 {
277     return
278 	cvNeedsParentheses(variable)
279 	? snprintf(buf, max, "1/(%s)", variable)
280 	: snprintf(buf, max, "1/%s", variable);
281 }
282 
283 
284 static ConverterOps	reciprocalOps = {
285     reciprocalClone,
286     reciprocalConvertDouble,
287     reciprocalConvertFloats,
288     reciprocalConvertDoubles,
289     reciprocalGetExpression,
290     nonFree};
291 
292 static cv_converter	reciprocalConverter = {&reciprocalOps};
293 
294 
295 /*******************************************************************************
296  * Scale Converter:
297  ******************************************************************************/
298 
299 static cv_converter*
scaleClone(cv_converter * const conv)300 scaleClone(
301     cv_converter* const	conv)
302 {
303     return cv_get_scale(conv->scale.value);
304 }
305 
306 
307 static double
scaleConvertDouble(const cv_converter * const conv,const double value)308 scaleConvertDouble(
309     const cv_converter* const	conv,
310     const double		value)
311 {
312     return conv->scale.value * value;
313 }
314 
315 
316 static float*
scaleConvertFloats(const cv_converter * const conv,const float * const in,const size_t count,float * out)317 scaleConvertFloats(
318     const cv_converter* const	conv,
319     const float* const		in,
320     const size_t		count,
321     float* 			out)
322 {
323     if (conv == NULL || in == NULL || out == NULL) {
324 	out = NULL;
325     }
326     else {
327 	size_t	i;
328 
329 	if (in < out) {
330 	    for (i = count; i-- > 0;)
331 		out[i] = (float)(conv->scale.value * in[i]);
332 	}
333 	else {
334 	    for (i = 0; i < count; i++)
335 		out[i] = (float)(conv->scale.value * in[i]);
336 	}
337     }
338 
339     return out;
340 }
341 
342 
343 static double*
scaleConvertDoubles(const cv_converter * const conv,const double * const in,const size_t count,double * out)344 scaleConvertDoubles(
345     const cv_converter* const	conv,
346     const double* const		in,
347     const size_t		count,
348     double* 			out)
349 {
350     if (conv == NULL || in == NULL || out == NULL) {
351 	out = NULL;
352     }
353     else {
354 	size_t	i;
355 
356 	if (in < out) {
357 	    for (i = count; i-- > 0;)
358 		out[i] = conv->scale.value * in[i];
359 	}
360 	else {
361 	    for (i = 0; i < count; i++)
362 		out[i] = conv->scale.value * in[i];
363 	}
364     }
365 
366     return out;
367 }
368 
369 
370 static int
scaleGetExpression(const cv_converter * const conv,char * const buf,const size_t max,const char * const variable)371 scaleGetExpression(
372     const cv_converter* const	conv,
373     char* const			buf,
374     const size_t		max,
375     const char* const		variable)
376 {
377     return
378 	cvNeedsParentheses(variable)
379 	? snprintf(buf, max, "%g*(%s)", conv->scale.value, variable)
380 	: snprintf(buf, max, "%g*%s", conv->scale.value, variable);
381 }
382 
383 
384 static ConverterOps	scaleOps = {
385     scaleClone,
386     scaleConvertDouble,
387     scaleConvertFloats,
388     scaleConvertDoubles,
389     scaleGetExpression,
390     cvSimpleFree};
391 
392 
393 /*******************************************************************************
394  * Offset Converter:
395  ******************************************************************************/
396 
397 static cv_converter*
offsetClone(cv_converter * const conv)398 offsetClone(
399     cv_converter* const	conv)
400 {
401     return cv_get_offset(conv->offset.value);
402 }
403 
404 
405 static double
offsetConvertDouble(const cv_converter * const conv,const double value)406 offsetConvertDouble(
407     const cv_converter* const	conv,
408     const double		value)
409 {
410     return conv->offset.value + value;
411 }
412 
413 
414 static float*
offsetConvertFloats(const cv_converter * const conv,const float * const in,const size_t count,float * out)415 offsetConvertFloats(
416     const cv_converter* const	conv,
417     const float* const		in,
418     const size_t		count,
419     float* 			out)
420 {
421     if (conv == NULL || in == NULL || out == NULL) {
422 	out = NULL;
423     }
424     else {
425 	size_t	i;
426 
427 	if (in < out) {
428 	    for (i = count; i-- > 0;)
429 		out[i] = (float)(conv->offset.value + in[i]);
430 	}
431 	else {
432 	    for (i = 0; i < count; i++)
433 		out[i] = (float)(conv->offset.value + in[i]);
434 	}
435     }
436 
437     return out;
438 }
439 
440 
441 static double*
offsetConvertDoubles(const cv_converter * const conv,const double * const in,const size_t count,double * out)442 offsetConvertDoubles(
443     const cv_converter* const	conv,
444     const double* const		in,
445     const size_t		count,
446     double* 			out)
447 {
448     if (conv == NULL || in == NULL || out == NULL) {
449 	out = NULL;
450     }
451     else {
452 	size_t	i;
453 
454 	if (in < out) {
455 	    for (i = count; i-- > 0;)
456 		out[i] = conv->offset.value + in[i];
457 	}
458 	else {
459 	    for (i = 0; i < count; i++)
460 		out[i] = conv->offset.value + in[i];
461 	}
462     }
463 
464     return out;
465 }
466 
467 
468 static int
offsetGetExpression(const cv_converter * const conv,char * const buf,const size_t max,const char * const variable)469 offsetGetExpression(
470     const cv_converter* const	conv,
471     char* const			buf,
472     const size_t		max,
473     const char* const		variable)
474 {
475     const int	oper = conv->offset.value < 0 ? '-' : '+';
476 
477     return
478 	cvNeedsParentheses(variable)
479 	    ? snprintf(buf, max, "(%s) %c %g", variable, oper,
480 		fabs(conv->offset.value))
481 	    : snprintf(buf, max, "%s %c %g", variable, oper,
482 		fabs(conv->offset.value));
483 }
484 
485 
486 static ConverterOps	offsetOps = {
487     offsetClone,
488     offsetConvertDouble,
489     offsetConvertFloats,
490     offsetConvertDoubles,
491     offsetGetExpression,
492     cvSimpleFree};
493 
494 
495 /*******************************************************************************
496  * Galilean Converter:
497  ******************************************************************************/
498 
499 static cv_converter*
cvGalileanClone(cv_converter * const conv)500 cvGalileanClone(
501     cv_converter* const	conv)
502 {
503     return cv_get_galilean(conv->galilean.slope, conv->galilean.intercept);
504 }
505 
506 
507 static double
galileanConvertDouble(const cv_converter * const conv,const double value)508 galileanConvertDouble(
509     const cv_converter* const	conv,
510     const double		value)
511 {
512     return conv->galilean.slope * value + conv->galilean.intercept;
513 }
514 
515 
516 static float*
galileanConvertFloats(const cv_converter * const conv,const float * const in,const size_t count,float * out)517 galileanConvertFloats(
518     const cv_converter* const	conv,
519     const float* const		in,
520     const size_t		count,
521     float* 			out)
522 {
523     if (conv == NULL || in == NULL || out == NULL) {
524 	out = NULL;
525     }
526     else {
527 	size_t	i;
528 
529 	if (in < out) {
530 	    for (i = count; i-- > 0;)
531 		out[i] = (float)(conv->galilean.slope * in[i] +
532 		    conv->galilean.intercept);
533 	}
534 	else {
535 	    for (i = 0; i < count; i++)
536 		out[i] = (float)(conv->galilean.slope * in[i] +
537 		    conv->galilean.intercept);
538 	}
539     }
540 
541     return out;
542 }
543 
544 
545 static double*
galileanConvertDoubles(const cv_converter * const conv,const double * const in,const size_t count,double * out)546 galileanConvertDoubles(
547     const cv_converter* const	conv,
548     const double* const		in,
549     const size_t		count,
550     double* 			out)
551 {
552     if (conv == NULL || in == NULL || out == NULL) {
553 	out = NULL;
554     }
555     else {
556 	size_t	i;
557 
558 	if (in < out) {
559 	    for (i = count; i-- > 0;)
560 		out[i] = conv->galilean.slope * in[i] +
561 		    conv->galilean.intercept;
562 	}
563 	else {
564 	    for (i = 0; i < count; i++)
565 		out[i] = conv->galilean.slope * in[i] +
566 		    conv->galilean.intercept;
567 	}
568     }
569 
570     return out;
571 }
572 
573 
574 static int
galileanGetExpression(const cv_converter * const conv,char * const buf,const size_t max,const char * const variable)575 galileanGetExpression(
576     const cv_converter* const	conv,
577     char* const			buf,
578     const size_t		max,
579     const char* const		variable)
580 {
581     const int	oper = conv->galilean.intercept < 0 ? '-' : '+';
582 
583     return
584 	cvNeedsParentheses(variable)
585 	    ? snprintf(buf, max, "%g*(%s) %c %g", conv->galilean.slope,
586 		variable, oper, fabs(conv->galilean.intercept))
587 	    : snprintf(buf, max, "%g*%s %c %g", conv->galilean.slope, variable,
588 		oper, fabs(conv->galilean.intercept));
589 }
590 
591 
592 static ConverterOps	galileanOps = {
593     cvGalileanClone,
594     galileanConvertDouble,
595     galileanConvertFloats,
596     galileanConvertDoubles,
597     galileanGetExpression,
598     cvSimpleFree};
599 
600 
601 /*******************************************************************************
602  * Logarithmic Converter:
603  ******************************************************************************/
604 
605 static cv_converter*
cvLogClone(cv_converter * const conv)606 cvLogClone(
607     cv_converter* const	conv)
608 {
609     return
610         cv_get_log(
611             conv->log.logE == M_LOG2E
612                 ? 2
613                 : conv->log.logE == 1
614                     ? M_E
615                     : conv->log.logE == M_LOG10E
616                         ? 10
617                         : exp(conv->log.logE));
618 }
619 
620 
621 static double
logConvertDouble(const cv_converter * const conv,const double value)622 logConvertDouble(
623     const cv_converter* const	conv,
624     const double		value)
625 {
626     return log(value) * conv->log.logE;
627 }
628 
629 
630 static float*
logConvertFloats(const cv_converter * const conv,const float * const in,const size_t count,float * out)631 logConvertFloats(
632     const cv_converter* const	conv,
633     const float* const		in,
634     const size_t		count,
635     float* 			out)
636 {
637     if (conv == NULL || in == NULL || out == NULL) {
638 	out = NULL;
639     }
640     else {
641 	size_t	i;
642 
643 	if (in < out) {
644 	    for (i = count; i-- > 0;)
645 		out[i] = (float)(log(in[i]) * conv->log.logE);
646 	}
647 	else {
648 	    for (i = 0; i < count; i++)
649 		out[i] = (float)(log(in[i]) * conv->log.logE);
650 	}
651     }
652 
653     return out;
654 }
655 
656 
657 static double*
logConvertDoubles(const cv_converter * const conv,const double * const in,const size_t count,double * out)658 logConvertDoubles(
659     const cv_converter* const	conv,
660     const double* const		in,
661     const size_t		count,
662     double* 			out)
663 {
664     if (conv == NULL || in == NULL || out == NULL) {
665 	out = NULL;
666     }
667     else {
668 	size_t	i;
669 
670 	if (in < out) {
671 	    for (i = count; i-- > 0;)
672 		out[i] = log(in[i]) * conv->log.logE;
673 	}
674 	else {
675 	    for (i = 0; i < count; i++)
676 		out[i] = log(in[i]) * conv->log.logE;
677 	}
678     }
679 
680     return out;
681 }
682 
683 
684 static int
logGetExpression(const cv_converter * const conv,char * const buf,const size_t max,const char * const variable)685 logGetExpression(
686     const cv_converter* const	conv,
687     char* const			buf,
688     const size_t		max,
689     const char* const		variable)
690 {
691     return
692         conv->log.logE == M_LOG2E
693             ? snprintf(buf, max, "lb(%s)", variable)
694             : conv->log.logE == 1
695                 ? snprintf(buf, max, "ln(%s)", variable)
696                 : conv->log.logE == M_LOG10E
697                     ? snprintf(buf, max, "lg(%s)", variable)
698                     : snprintf(buf, max, "%g*ln(%s)", conv->log.logE, variable);
699 }
700 
701 
702 static ConverterOps	logOps = {
703     cvLogClone,
704     logConvertDouble,
705     logConvertFloats,
706     logConvertDoubles,
707     logGetExpression,
708     cvSimpleFree};
709 
710 
711 /*******************************************************************************
712  * Exponential Converter:
713  ******************************************************************************/
714 
715 static cv_converter*
expClone(cv_converter * const conv)716 expClone(
717     cv_converter* const	conv)
718 {
719     return cv_get_pow(conv->exp.base);
720 }
721 
722 
723 static double
expConvertDouble(const cv_converter * const conv,const double value)724 expConvertDouble(
725     const cv_converter* const	conv,
726     const double		value)
727 {
728     return pow(conv->exp.base, value);
729 }
730 
731 static float*
expConvertFloats(const cv_converter * const conv,const float * const in,const size_t count,float * out)732 expConvertFloats(
733     const cv_converter* const	conv,
734     const float* const		in,
735     const size_t		count,
736     float* 			out)
737 {
738     if (conv == NULL || in == NULL || out == NULL) {
739 	out = NULL;
740     }
741     else {
742 	size_t	i;
743 
744 	if (in < out) {
745 	    for (i = count; i-- > 0;)
746 		out[i] = (float)(pow(conv->exp.base, in[i]));
747 	}
748 	else {
749 	    for (i = 0; i < count; i++)
750 		out[i] = (float)(pow(conv->exp.base, in[i]));
751 	}
752     }
753 
754     return out;
755 }
756 
757 
758 static double*
expConvertDoubles(const cv_converter * const conv,const double * const in,const size_t count,double * out)759 expConvertDoubles(
760     const cv_converter* const	conv,
761     const double* const		in,
762     const size_t		count,
763     double* 			out)
764 {
765     if (conv == NULL || in == NULL || out == NULL) {
766 	out = NULL;
767     }
768     else {
769 	size_t	i;
770 
771 	if (in < out) {
772 	    for (i = count; i-- > 0;)
773 		out[i] = pow(conv->exp.base, in[i]);
774 	}
775 	else {
776 	    for (i = 0; i < count; i++)
777 		out[i] = pow(conv->exp.base, in[i]);
778 	}
779     }
780 
781     return out;
782 }
783 
784 
785 static int
expGetExpression(const cv_converter * const conv,char * const buf,const size_t max,const char * const variable)786 expGetExpression(
787     const cv_converter* const	conv,
788     char* const			buf,
789     const size_t		max,
790     const char* const		variable)
791 {
792     return
793 	cvNeedsParentheses(variable)
794 	    ? snprintf(buf, max, "pow(%g, (%s))", conv->exp.base, variable)
795 	    : snprintf(buf, max, "pow(%g, %s)", conv->exp.base, variable);
796 }
797 
798 
799 static ConverterOps	expOps = {
800     expClone,
801     expConvertDouble,
802     expConvertFloats,
803     expConvertDoubles,
804     expGetExpression,
805     cvSimpleFree};
806 
807 
808 /*******************************************************************************
809  * Composite Converter:
810  ******************************************************************************/
811 
812 static cv_converter*
compositeClone(cv_converter * const conv)813 compositeClone(
814     cv_converter* const	conv)
815 {
816     return cv_combine(conv->composite.first, conv->composite.second);
817 }
818 
819 
820 static double
compositeConvertDouble(const cv_converter * const conv,const double value)821 compositeConvertDouble(
822     const cv_converter* const	conv,
823     const double		value)
824 {
825     return
826 	cv_convert_double(conv->composite.second,
827 	    cv_convert_double(((CompositeConverter*)conv)->first, value));
828 }
829 
830 
831 static float*
compositeConvertFloats(const cv_converter * const conv,const float * const in,const size_t count,float * out)832 compositeConvertFloats(
833     const cv_converter* const	conv,
834     const float* const		in,
835     const size_t		count,
836     float* 			out)
837 {
838     if (conv == NULL || in == NULL || out == NULL) {
839 	out = NULL;
840     }
841     else {
842 	out =
843 	    cv_convert_floats(
844 		conv->composite.second,
845 		cv_convert_floats(conv->composite.first, in, count, out),
846 		count,
847 		out);
848     }
849 
850     return out;
851 }
852 
853 
854 static double*
compositeConvertDoubles(const cv_converter * const conv,const double * const in,const size_t count,double * out)855 compositeConvertDoubles(
856     const cv_converter* const	conv,
857     const double* const		in,
858     const size_t		count,
859     double* 			out)
860 {
861     if (conv == NULL || in == NULL || out == NULL) {
862 	out = NULL;
863     }
864     else {
865 	out =
866 	    cv_convert_doubles(
867 		conv->composite.second,
868 		cv_convert_doubles(conv->composite.first, in, count, out),
869 		count,
870 		out);
871     }
872 
873     return out;
874 }
875 
876 
877 static void
compositeFree(cv_converter * const conv)878 compositeFree(
879     cv_converter* const	conv)
880 {
881     cv_free(conv->composite.first);
882     cv_free(conv->composite.second);
883     free(conv);
884 }
885 
886 
887 static int
compositeGetExpression(const cv_converter * const conv,char * const buf,const size_t max,const char * const variable)888 compositeGetExpression(
889     const cv_converter* const	conv,
890     char* const			buf,
891     const size_t		max,
892     const char* const		variable)
893 {
894     char	tmpBuf[132];
895     int		nchar = cv_get_expression(conv->composite.first, buf, max,
896 	variable);
897 
898     if (nchar >= 0) {
899 	buf[max-1] = 0;
900 
901 	if (cvNeedsParentheses(buf)) {
902 	    (void)snprintf(tmpBuf, sizeof(tmpBuf), "(%s)", buf);
903 	}
904 	else {
905 	    (void)strncpy(tmpBuf, buf, sizeof(tmpBuf));
906 
907 	    tmpBuf[sizeof(tmpBuf)-1] = 0;
908 	}
909 
910 	nchar = cv_get_expression(conv->composite.second, buf, max, tmpBuf);
911     }
912 
913     return nchar;
914 }
915 
916 
917 static ConverterOps	compositeOps = {
918     compositeClone,
919     compositeConvertDouble,
920     compositeConvertFloats,
921     compositeConvertDoubles,
922     compositeGetExpression,
923     compositeFree};
924 
925 
926 /*******************************************************************************
927  * Public API:
928  ******************************************************************************/
929 
930 /*
931  * Returns the trivial converter (i.e., y = x).
932  * When finished with the converter, the client should pass the converter to
933  * cv_free().
934  *
935  * Returns:
936  *	The trivial converter.
937  */
938 cv_converter*
cv_get_trivial()939 cv_get_trivial()
940 {
941     return &trivialConverter;
942 }
943 
944 
945 /*
946  * Returns the reciprocal converter (i.e., y = 1/x).
947  * When finished with the converter, the client should pass the converter to
948  * cv_free().
949  *
950  * Returns:
951  *	The reciprocal converter.
952  */
953 cv_converter*
cv_get_inverse()954 cv_get_inverse()
955 {
956     return &reciprocalConverter;
957 }
958 
959 
960 /*
961  * Returns a converter that multiplies values by a number (i.e., y = ax).
962  * When finished with the converter, the client should pass the converter to
963  * cv_free().
964  *
965  * Arguments:
966  *	slope	The number by which to multiply values.
967  * Returns:
968  *	NULL	Necessary memory couldn't be allocated.
969  *	else	A converter that will multiply values by the given number.
970  */
971 cv_converter*
cv_get_scale(const double slope)972 cv_get_scale(
973     const double	slope)
974 {
975     cv_converter*	conv;
976 
977     if (slope == 1) {
978 	conv = &trivialConverter;
979     }
980     else {
981 	conv = malloc(sizeof(*conv));
982 
983 	if (conv != NULL) {
984 	    conv->ops = &scaleOps;
985 	    conv->scale.value = slope;
986 	}
987     }
988 
989     return conv;
990 }
991 
992 
993 /*
994  * Returns a converter that adds a number to values (i.e., y = x + b).
995  * When finished with the converter, the client should pass the converter to
996  * cv_free().
997  *
998  * Arguments:
999  *	offset	The number to be added.
1000  * Returns:
1001  *	NULL	Necessary memory couldn't be allocated.
1002  *	else	A converter that adds the given number to values.
1003  */
1004 cv_converter*
cv_get_offset(const double offset)1005 cv_get_offset(
1006     const double	offset)
1007 {
1008     cv_converter*	conv;
1009 
1010     if (offset == 0) {
1011 	conv = &trivialConverter;
1012     }
1013     else {
1014 	conv = malloc(sizeof(*conv));
1015 
1016 	if (conv != NULL) {
1017 	    conv->ops = &offsetOps;
1018 	    conv->offset.value = offset;
1019 	}
1020     }
1021 
1022     return conv;
1023 }
1024 
1025 
1026 /*
1027  * Returns a Galilean converter (i.e., y = ax + b).
1028  * When finished with the converter, the client should pass the converter to
1029  * cv_free().
1030  *
1031  * Arguments:
1032  *	slope	The number by which to multiply values.
1033  *	offset	The number to be added.
1034  * Returns:
1035  *	NULL	Necessary memory couldn't be allocated.
1036  *	else	A Galilean converter corresponding to the inputs.
1037  */
1038 cv_converter*
cv_get_galilean(const double slope,const double intercept)1039 cv_get_galilean(
1040     const double	slope,
1041     const double	intercept)
1042 {
1043     cv_converter*	conv;
1044 
1045     if (slope == 1) {
1046 	conv = cv_get_offset(intercept);
1047     }
1048     else if (intercept == 0) {
1049 	conv = cv_get_scale(slope);
1050     }
1051     else {
1052 	conv = malloc(sizeof(*conv));
1053 
1054 	if (conv != NULL) {
1055 	    conv->ops = &galileanOps;
1056 	    conv->galilean.slope = slope;
1057 	    conv->galilean.intercept = intercept;
1058 	}
1059     }
1060 
1061     return conv;
1062 }
1063 
1064 
1065 /*
1066  * Returns a logarithmic converter (i.e., y = log(x/x0) in some base).
1067  * When finished with the converter, the client should pass the converter to
1068  * cv_free().
1069  *
1070  * Arguments:
1071  *	base		The logarithmic base (e.g., 2, M_E, 10).  Must be
1072  *                      greater than one.
1073  * Returns:
1074  *	NULL		"base" is invalid or necessary memory couldn't be
1075  *			allocated.
1076  *	else		A logarithmic converter corresponding to the inputs.
1077  */
1078 cv_converter*
cv_get_log(const double base)1079 cv_get_log(
1080     const double	base)
1081 {
1082     cv_converter*	conv;
1083 
1084     if (base <= 1) {
1085 	conv = NULL;
1086     }
1087     else {
1088 	conv = malloc(sizeof(*conv));
1089 
1090 	if (conv != NULL) {
1091 	    conv->ops = &logOps;
1092 	    conv->log.logE =
1093                 base == 2
1094                     ? M_LOG2E
1095                     : base == M_E
1096                         ? 1
1097                         : base == 10
1098                             ? M_LOG10E
1099                             : 1/log(base);
1100 	}
1101     }
1102 
1103     return conv;
1104 }
1105 
1106 
1107 /*
1108  * Returns an exponential converter (i.e., y = pow(b, x) in some base "b").
1109  * When finished with the converter, the client should pass the converter to
1110  * cv_free().
1111  *
1112  * Arguments:
1113  *	base		The desired base.  Must be positive.
1114  * Returns:
1115  *	NULL		"base" is invalid or necessary memory couldn't be
1116  *			allocated.
1117  *	else		An exponential converter corresponding to the inputs.
1118  */
1119 cv_converter*
cv_get_pow(const double base)1120 cv_get_pow(
1121     const double	base)
1122 {
1123     cv_converter*	conv;
1124 
1125     if (base <= 0) {
1126 	conv = NULL;
1127     }
1128     else {
1129 	conv = malloc(sizeof(*conv));
1130 
1131 	if (conv != NULL) {
1132 	    conv->ops = &expOps;
1133 	    conv->exp.base = base;
1134 	}
1135     }
1136 
1137     return conv;
1138 }
1139 
1140 
1141 /*
1142  * Returns a converter corresponding to the sequential application of two
1143  * other converters.  The returned converter should be passed to cv_free() when
1144  * it is no longer needed.
1145  *
1146  * Arguments:
1147  *	first	The converter to be applied first.  May be passed to cv_free()
1148  *		upon return.
1149  *	second	The converter to be applied second.  May be passed to cv_free()
1150  *		upon return.
1151  * Returns:
1152  *	NULL	Either "first" or "second" is NULL or necessary memory couldn't
1153  *		be allocated.
1154  *      else    A converter corresponding to the sequential application of the
1155  *              given converters.  If one of the input converters is the trivial
1156  *              converter, then the returned converter will be the other input
1157  *              converter.
1158  */
1159 cv_converter*
cv_combine(cv_converter * const first,cv_converter * const second)1160 cv_combine(
1161     cv_converter* const	first,
1162     cv_converter* const	second)
1163 {
1164     cv_converter*	conv;
1165 
1166     if (first == NULL || second == NULL) {
1167 	conv = NULL;
1168     }
1169     else if (IS_TRIVIAL(first)) {
1170 	conv = CV_CLONE(second);
1171     }
1172     else if (IS_TRIVIAL(second)) {
1173 	conv = CV_CLONE(first);
1174     }
1175     else {
1176 	conv = NULL;
1177 
1178 	if (IS_RECIPROCAL(first)) {
1179 	    if (IS_RECIPROCAL(second)) {
1180 		conv = cv_get_trivial();
1181 	    }
1182 	}
1183 	else if (IS_SCALE(first)) {
1184 	    if (IS_SCALE(second)) {
1185 		conv = cv_get_scale(first->scale.value * second->scale.value);
1186 	    }
1187 	    else if (IS_OFFSET(second)) {
1188 		conv = cv_get_galilean(first->scale.value, second->offset.value);
1189 	    }
1190 	    else if (IS_GALILEAN(second)) {
1191 		conv = cv_get_galilean(
1192 		    first->scale.value * second->galilean.slope,
1193 		    second->galilean.intercept);
1194 	    }
1195 	}
1196 	else if (IS_OFFSET(first)) {
1197 	    if (IS_SCALE(second)) {
1198 		conv = cv_get_galilean(second->scale.value,
1199 		    first->offset.value * second->scale.value);
1200 	    }
1201 	    else if (IS_OFFSET(second)) {
1202 		conv = cv_get_offset(first->offset.value + second->offset.value);
1203 	    }
1204 	    else if (IS_GALILEAN(second)) {
1205 		conv = cv_get_galilean(second->galilean.slope,
1206 		    first->offset.value * second->galilean.slope +
1207 			second->galilean.intercept);
1208 	    }
1209 	}
1210 	else if (IS_GALILEAN(first)) {
1211 	    if (IS_SCALE(second)) {
1212 		conv = cv_get_galilean(
1213 		    second->scale.value * first->galilean.slope,
1214 		    second->scale.value * first->galilean.intercept);
1215 	    }
1216 	    else if (IS_OFFSET(second)) {
1217 		conv = cv_get_galilean(first->galilean.slope,
1218 		    first->galilean.intercept + second->offset.value);
1219 	    }
1220 	    else if (IS_GALILEAN(second)) {
1221 		conv = cv_get_galilean(
1222 		    second->galilean.slope * first->galilean.slope,
1223 		    second->galilean.slope * first->galilean.intercept +
1224 			second->galilean.intercept);
1225 	    }
1226 	}
1227 
1228 	if (conv == NULL) {
1229 	    /*
1230 	     * General case: create a composite converter.
1231 	     */
1232 	    cv_converter*	c1 = CV_CLONE(first);
1233             int                 error = 1;
1234 
1235             if (c1 != NULL) {
1236                 cv_converter*	c2 = CV_CLONE(second);
1237 
1238                 if (c2 != NULL) {
1239                     conv = malloc(sizeof(*conv));
1240 
1241                     if (conv != NULL) {
1242                         conv->composite.ops = &compositeOps;
1243                         conv->composite.first = c1;
1244                         conv->composite.second = c2;
1245                         error = 0;
1246                     }                   /* "conv" allocated */
1247 
1248                     if (error)
1249                         cv_free(c2);
1250                 }                       /* "c2" allocated */
1251 
1252                 if (error)
1253                     cv_free(c1);
1254             }                           /* "c1" allocated */
1255 	}                               /* "conv != NULL" */
1256     }                                   /* "first" & "second" not trivial */
1257 
1258     return conv;
1259 }
1260 
1261 
1262 /*
1263  * Frees resources associated with a converter.  Use of the converter argument
1264  * subsequent to this function may result in undefined behavior.
1265  *
1266  * Arguments:
1267  *	conv	The converter to have its resources freed or NULL.  The
1268  *		converter must have been returned by this module.
1269  */
1270 void
cv_free(cv_converter * const conv)1271 cv_free(
1272     cv_converter* const	conv)
1273 {
1274     if (conv != NULL) {
1275 	conv->ops->free((cv_converter*)conv);
1276     }
1277 }
1278 
1279 
1280 /*
1281  * Converts a float.
1282  *
1283  * Arguments:
1284  *	converter	Pointer to the converter.
1285  *	value		The value to be converted.
1286  * Returns:
1287  *	The converted value.
1288  */
1289 float
cv_convert_float(const cv_converter * converter,const float value)1290 cv_convert_float(
1291     const cv_converter*	converter,
1292     const float		value)
1293 {
1294     return (float)converter->ops->convertDouble(converter, value);
1295 }
1296 
1297 
1298 /*
1299  * Converts a double.
1300  *
1301  * Arguments:
1302  *	converter	Pointer to the converter.
1303  *	value		The value to be converted.
1304  * Returns:
1305  *	The converted value.
1306  */
1307 double
cv_convert_double(const cv_converter * converter,const double value)1308 cv_convert_double(
1309     const cv_converter*	converter,
1310     const double	value)
1311 {
1312     return converter->ops->convertDouble(converter, value);
1313 }
1314 
1315 
1316 /*
1317  * Converts an array of floats.
1318  *
1319  * Arguments:
1320  *	converter	Pointer to the converter.
1321  *	in		Pointer to the values to be converted.  The array may
1322  *			overlap "out".
1323  *	count		The number of values to be converted.
1324  *	out		Pointer to the output array for the converted values.
1325  *			The array may overlap "in".
1326  * Returns:
1327  *	NULL		"converter", "in", or "out" is NULL.
1328  *	else		Pointer to the output array, "out".
1329  */
1330 float*
cv_convert_floats(const cv_converter * converter,const float * const in,const size_t count,float * out)1331 cv_convert_floats(
1332     const cv_converter*	converter,
1333     const float* const	in,
1334     const size_t	count,
1335     float*		out)
1336 {
1337     if (converter == NULL || in == NULL || out == NULL) {
1338 	out = NULL;
1339     }
1340     else {
1341         out = converter->ops->convertFloats(converter, in, count, out);
1342     }
1343 
1344     return out;
1345 }
1346 
1347 
1348 /*
1349  * Converts an array of doubles.
1350  *
1351  * Arguments:
1352  *	converter	Pointer to the converter.
1353  *	in		Pointer to the values to be converted.  The array may
1354  *			overlap "out".
1355  *	count		The number of values to be converted.
1356  *	out		Pointer to the output array for the converted values.
1357  *			The array may overlap "in".
1358  * Returns:
1359  *	NULL		"converter", "in", or "out" is NULL.
1360  *	else		Pointer to the output array, "out".
1361  */
1362 double*
cv_convert_doubles(const cv_converter * converter,const double * const in,const size_t count,double * out)1363 cv_convert_doubles(
1364     const cv_converter*	converter,
1365     const double* const	in,
1366     const size_t	count,
1367     double*		out)
1368 {
1369     if (converter == NULL || in == NULL || out == NULL) {
1370 	out = NULL;
1371     }
1372     else {
1373     	out = converter->ops->convertDoubles(converter, in, count, out);
1374     }
1375 
1376     return out;
1377 }
1378 
1379 
1380 /*
1381  * Returns a string expression representation of a converter.
1382  *
1383  * Arguments:
1384  *	conv		The converter.
1385  *	buf		The buffer into which to write the expression.
1386  *	max		The size of the buffer.
1387  *	variable	The string to be used as the input value for the
1388  *			converter.
1389  * RETURNS
1390  *	<0	An error was encountered.
1391  *	else	The number of bytes formatted excluding the terminating null.
1392  */
1393 int
cv_get_expression(const cv_converter * const conv,char * const buf,size_t max,const char * const variable)1394 cv_get_expression(
1395     const cv_converter* const	conv,
1396     char* const			buf,
1397     size_t			max,
1398     const char* const		variable)
1399 {
1400     return conv->ops->getExpression(conv, buf, max, variable);
1401 }
1402