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