/* * Copyright 2008, 2009 University Corporation for Atmospheric Research * * This file is part of the UDUNITS-2 package. See the file LICENSE * in the top-level source-directory of the package for copying and * redistribution conditions. */ /* * Value converters for the udunits(3) library. */ /*LINTLIBRARY*/ #ifndef _XOPEN_SOURCE # define _XOPEN_SOURCE 500 #endif #include #include #include #include #include #include "converter.h" /* this module's API */ typedef struct { cv_converter* (*clone)(cv_converter*); double (*convertDouble) (const cv_converter*, double); float* (*convertFloats) (const cv_converter*, const float*, size_t, float*); double* (*convertDoubles) (const cv_converter*, const double*, size_t, double*); int (*getExpression) (const cv_converter*, char* buf, size_t, const char*); void (*free)(cv_converter*); } ConverterOps; typedef struct { ConverterOps* ops; } ReciprocalConverter; typedef struct { ConverterOps* ops; double value; } ScaleConverter; typedef struct { ConverterOps* ops; double value; } OffsetConverter; typedef struct { ConverterOps* ops; double slope; double intercept; } GalileanConverter; typedef struct { ConverterOps* ops; double logE; } LogConverter; typedef struct { ConverterOps* ops; double base; } ExpConverter; typedef struct { ConverterOps* ops; cv_converter* first; cv_converter* second; } CompositeConverter; union cv_converter { ConverterOps* ops; ScaleConverter scale; OffsetConverter offset; GalileanConverter galilean; LogConverter log; ExpConverter exp; CompositeConverter composite; }; #define CV_CLONE(conv) ((conv)->ops->clone(conv)) #define IS_TRIVIAL(conv) ((conv)->ops == &trivialOps) #define IS_RECIPROCAL(conv) ((conv)->ops == &reciprocalOps) #define IS_SCALE(conv) ((conv)->ops == &scaleOps) #define IS_OFFSET(conv) ((conv)->ops == &offsetOps) #define IS_GALILEAN(conv) ((conv)->ops == &galileanOps) #define IS_LOG(conv) ((conv)->ops == &logOps) static void nonFree( cv_converter* const conv) { } static void cvSimpleFree( cv_converter* const conv) { free(conv); } static int cvNeedsParentheses( const char* const string) { return strpbrk(string, " \t") != NULL && (string[0] != '(' || string[strlen(string)-1] != ')'); } /******************************************************************************* * Trivial Converter: ******************************************************************************/ static cv_converter* trivialClone( cv_converter* const conv) { return cv_get_trivial(); } static double trivialConvertDouble( const cv_converter* const conv, const double value) { return value; } static float* trivialConvertFloats( const cv_converter* const conv, const float* const in, const size_t count, float* out) { if (in == NULL || out == NULL) { out = NULL; } else { (void)memmove(out, in, count*sizeof(float)); } return out; } static double* trivialConvertDoubles( const cv_converter* const conv, const double* const in, const size_t count, double* out) { if (in == NULL || out == NULL) { out = NULL; } else { (void)memmove(out, in, count*sizeof(double)); } return out; } static int trivialGetExpression( const cv_converter* const conv, char* const buf, const size_t max, const char* const variable) { return snprintf(buf, max, "%s", variable); } static ConverterOps trivialOps = { trivialClone, trivialConvertDouble, trivialConvertFloats, trivialConvertDoubles, trivialGetExpression, nonFree}; static cv_converter trivialConverter = {&trivialOps}; /******************************************************************************* * Reciprocal Converter: ******************************************************************************/ static cv_converter* reciprocalClone( cv_converter* const conv) { return cv_get_inverse(); } static double reciprocalConvertDouble( const cv_converter* const conv, const double value) { return 1.0 / value; } static float* reciprocalConvertFloats( const cv_converter* const conv, const float* const in, const size_t count, float* out) { if (in == NULL || out == NULL) { out = NULL; } else { size_t i; if (in < out) { for (i = count; i-- > 0;) out[i] = (float)(1.0f / in[i]); } else { for (i = 0; i < count; i++) out[i] = (float)(1.0f / in[i]); } } return out; } static double* reciprocalConvertDoubles( const cv_converter* const conv, const double* const in, const size_t count, double* out) { if (in == NULL || out == NULL) { out = NULL; } else { size_t i; if (in < out) { for (i = count; i-- > 0;) out[i] = 1.0 / in[i]; } else { for (i = 0; i < count; i++) out[i] = 1.0 / in[i]; } } return out; } static int reciprocalGetExpression( const cv_converter* const conv, char* const buf, const size_t max, const char* const variable) { return cvNeedsParentheses(variable) ? snprintf(buf, max, "1/(%s)", variable) : snprintf(buf, max, "1/%s", variable); } static ConverterOps reciprocalOps = { reciprocalClone, reciprocalConvertDouble, reciprocalConvertFloats, reciprocalConvertDoubles, reciprocalGetExpression, nonFree}; static cv_converter reciprocalConverter = {&reciprocalOps}; /******************************************************************************* * Scale Converter: ******************************************************************************/ static cv_converter* scaleClone( cv_converter* const conv) { return cv_get_scale(conv->scale.value); } static double scaleConvertDouble( const cv_converter* const conv, const double value) { return conv->scale.value * value; } static float* scaleConvertFloats( const cv_converter* const conv, const float* const in, const size_t count, float* out) { if (conv == NULL || in == NULL || out == NULL) { out = NULL; } else { size_t i; if (in < out) { for (i = count; i-- > 0;) out[i] = (float)(conv->scale.value * in[i]); } else { for (i = 0; i < count; i++) out[i] = (float)(conv->scale.value * in[i]); } } return out; } static double* scaleConvertDoubles( const cv_converter* const conv, const double* const in, const size_t count, double* out) { if (conv == NULL || in == NULL || out == NULL) { out = NULL; } else { size_t i; if (in < out) { for (i = count; i-- > 0;) out[i] = conv->scale.value * in[i]; } else { for (i = 0; i < count; i++) out[i] = conv->scale.value * in[i]; } } return out; } static int scaleGetExpression( const cv_converter* const conv, char* const buf, const size_t max, const char* const variable) { return cvNeedsParentheses(variable) ? snprintf(buf, max, "%g*(%s)", conv->scale.value, variable) : snprintf(buf, max, "%g*%s", conv->scale.value, variable); } static ConverterOps scaleOps = { scaleClone, scaleConvertDouble, scaleConvertFloats, scaleConvertDoubles, scaleGetExpression, cvSimpleFree}; /******************************************************************************* * Offset Converter: ******************************************************************************/ static cv_converter* offsetClone( cv_converter* const conv) { return cv_get_offset(conv->offset.value); } static double offsetConvertDouble( const cv_converter* const conv, const double value) { return conv->offset.value + value; } static float* offsetConvertFloats( const cv_converter* const conv, const float* const in, const size_t count, float* out) { if (conv == NULL || in == NULL || out == NULL) { out = NULL; } else { size_t i; if (in < out) { for (i = count; i-- > 0;) out[i] = (float)(conv->offset.value + in[i]); } else { for (i = 0; i < count; i++) out[i] = (float)(conv->offset.value + in[i]); } } return out; } static double* offsetConvertDoubles( const cv_converter* const conv, const double* const in, const size_t count, double* out) { if (conv == NULL || in == NULL || out == NULL) { out = NULL; } else { size_t i; if (in < out) { for (i = count; i-- > 0;) out[i] = conv->offset.value + in[i]; } else { for (i = 0; i < count; i++) out[i] = conv->offset.value + in[i]; } } return out; } static int offsetGetExpression( const cv_converter* const conv, char* const buf, const size_t max, const char* const variable) { const int oper = conv->offset.value < 0 ? '-' : '+'; return cvNeedsParentheses(variable) ? snprintf(buf, max, "(%s) %c %g", variable, oper, fabs(conv->offset.value)) : snprintf(buf, max, "%s %c %g", variable, oper, fabs(conv->offset.value)); } static ConverterOps offsetOps = { offsetClone, offsetConvertDouble, offsetConvertFloats, offsetConvertDoubles, offsetGetExpression, cvSimpleFree}; /******************************************************************************* * Galilean Converter: ******************************************************************************/ static cv_converter* cvGalileanClone( cv_converter* const conv) { return cv_get_galilean(conv->galilean.slope, conv->galilean.intercept); } static double galileanConvertDouble( const cv_converter* const conv, const double value) { return conv->galilean.slope * value + conv->galilean.intercept; } static float* galileanConvertFloats( const cv_converter* const conv, const float* const in, const size_t count, float* out) { if (conv == NULL || in == NULL || out == NULL) { out = NULL; } else { size_t i; if (in < out) { for (i = count; i-- > 0;) out[i] = (float)(conv->galilean.slope * in[i] + conv->galilean.intercept); } else { for (i = 0; i < count; i++) out[i] = (float)(conv->galilean.slope * in[i] + conv->galilean.intercept); } } return out; } static double* galileanConvertDoubles( const cv_converter* const conv, const double* const in, const size_t count, double* out) { if (conv == NULL || in == NULL || out == NULL) { out = NULL; } else { size_t i; if (in < out) { for (i = count; i-- > 0;) out[i] = conv->galilean.slope * in[i] + conv->galilean.intercept; } else { for (i = 0; i < count; i++) out[i] = conv->galilean.slope * in[i] + conv->galilean.intercept; } } return out; } static int galileanGetExpression( const cv_converter* const conv, char* const buf, const size_t max, const char* const variable) { const int oper = conv->galilean.intercept < 0 ? '-' : '+'; return cvNeedsParentheses(variable) ? snprintf(buf, max, "%g*(%s) %c %g", conv->galilean.slope, variable, oper, fabs(conv->galilean.intercept)) : snprintf(buf, max, "%g*%s %c %g", conv->galilean.slope, variable, oper, fabs(conv->galilean.intercept)); } static ConverterOps galileanOps = { cvGalileanClone, galileanConvertDouble, galileanConvertFloats, galileanConvertDoubles, galileanGetExpression, cvSimpleFree}; /******************************************************************************* * Logarithmic Converter: ******************************************************************************/ static cv_converter* cvLogClone( cv_converter* const conv) { return cv_get_log( conv->log.logE == M_LOG2E ? 2 : conv->log.logE == 1 ? M_E : conv->log.logE == M_LOG10E ? 10 : exp(conv->log.logE)); } static double logConvertDouble( const cv_converter* const conv, const double value) { return log(value) * conv->log.logE; } static float* logConvertFloats( const cv_converter* const conv, const float* const in, const size_t count, float* out) { if (conv == NULL || in == NULL || out == NULL) { out = NULL; } else { size_t i; if (in < out) { for (i = count; i-- > 0;) out[i] = (float)(log(in[i]) * conv->log.logE); } else { for (i = 0; i < count; i++) out[i] = (float)(log(in[i]) * conv->log.logE); } } return out; } static double* logConvertDoubles( const cv_converter* const conv, const double* const in, const size_t count, double* out) { if (conv == NULL || in == NULL || out == NULL) { out = NULL; } else { size_t i; if (in < out) { for (i = count; i-- > 0;) out[i] = log(in[i]) * conv->log.logE; } else { for (i = 0; i < count; i++) out[i] = log(in[i]) * conv->log.logE; } } return out; } static int logGetExpression( const cv_converter* const conv, char* const buf, const size_t max, const char* const variable) { return conv->log.logE == M_LOG2E ? snprintf(buf, max, "lb(%s)", variable) : conv->log.logE == 1 ? snprintf(buf, max, "ln(%s)", variable) : conv->log.logE == M_LOG10E ? snprintf(buf, max, "lg(%s)", variable) : snprintf(buf, max, "%g*ln(%s)", conv->log.logE, variable); } static ConverterOps logOps = { cvLogClone, logConvertDouble, logConvertFloats, logConvertDoubles, logGetExpression, cvSimpleFree}; /******************************************************************************* * Exponential Converter: ******************************************************************************/ static cv_converter* expClone( cv_converter* const conv) { return cv_get_pow(conv->exp.base); } static double expConvertDouble( const cv_converter* const conv, const double value) { return pow(conv->exp.base, value); } static float* expConvertFloats( const cv_converter* const conv, const float* const in, const size_t count, float* out) { if (conv == NULL || in == NULL || out == NULL) { out = NULL; } else { size_t i; if (in < out) { for (i = count; i-- > 0;) out[i] = (float)(pow(conv->exp.base, in[i])); } else { for (i = 0; i < count; i++) out[i] = (float)(pow(conv->exp.base, in[i])); } } return out; } static double* expConvertDoubles( const cv_converter* const conv, const double* const in, const size_t count, double* out) { if (conv == NULL || in == NULL || out == NULL) { out = NULL; } else { size_t i; if (in < out) { for (i = count; i-- > 0;) out[i] = pow(conv->exp.base, in[i]); } else { for (i = 0; i < count; i++) out[i] = pow(conv->exp.base, in[i]); } } return out; } static int expGetExpression( const cv_converter* const conv, char* const buf, const size_t max, const char* const variable) { return cvNeedsParentheses(variable) ? snprintf(buf, max, "pow(%g, (%s))", conv->exp.base, variable) : snprintf(buf, max, "pow(%g, %s)", conv->exp.base, variable); } static ConverterOps expOps = { expClone, expConvertDouble, expConvertFloats, expConvertDoubles, expGetExpression, cvSimpleFree}; /******************************************************************************* * Composite Converter: ******************************************************************************/ static cv_converter* compositeClone( cv_converter* const conv) { return cv_combine(conv->composite.first, conv->composite.second); } static double compositeConvertDouble( const cv_converter* const conv, const double value) { return cv_convert_double(conv->composite.second, cv_convert_double(((CompositeConverter*)conv)->first, value)); } static float* compositeConvertFloats( const cv_converter* const conv, const float* const in, const size_t count, float* out) { if (conv == NULL || in == NULL || out == NULL) { out = NULL; } else { out = cv_convert_floats( conv->composite.second, cv_convert_floats(conv->composite.first, in, count, out), count, out); } return out; } static double* compositeConvertDoubles( const cv_converter* const conv, const double* const in, const size_t count, double* out) { if (conv == NULL || in == NULL || out == NULL) { out = NULL; } else { out = cv_convert_doubles( conv->composite.second, cv_convert_doubles(conv->composite.first, in, count, out), count, out); } return out; } static void compositeFree( cv_converter* const conv) { cv_free(conv->composite.first); cv_free(conv->composite.second); free(conv); } static int compositeGetExpression( const cv_converter* const conv, char* const buf, const size_t max, const char* const variable) { char tmpBuf[132]; int nchar = cv_get_expression(conv->composite.first, buf, max, variable); if (nchar >= 0) { buf[max-1] = 0; if (cvNeedsParentheses(buf)) { nchar = snprintf(tmpBuf, sizeof(tmpBuf), "(%s)", buf); } else { (void)strncpy(tmpBuf, buf, sizeof(tmpBuf)); tmpBuf[sizeof(tmpBuf)-1] = 0; } nchar = cv_get_expression(conv->composite.second, buf, max, tmpBuf); } return nchar; } static ConverterOps compositeOps = { compositeClone, compositeConvertDouble, compositeConvertFloats, compositeConvertDoubles, compositeGetExpression, compositeFree}; /******************************************************************************* * Public API: ******************************************************************************/ /* * Returns the trivial converter (i.e., y = x). * When finished with the converter, the client should pass the converter to * cv_free(). * * Returns: * The trivial converter. */ cv_converter* cv_get_trivial() { return &trivialConverter; } /* * Returns the reciprocal converter (i.e., y = 1/x). * When finished with the converter, the client should pass the converter to * cv_free(). * * Returns: * The reciprocal converter. */ cv_converter* cv_get_inverse() { return &reciprocalConverter; } /* * Returns a converter that multiplies values by a number (i.e., y = ax). * When finished with the converter, the client should pass the converter to * cv_free(). * * Arguments: * slope The number by which to multiply values. * Returns: * NULL Necessary memory couldn't be allocated. * else A converter that will multiply values by the given number. */ cv_converter* cv_get_scale( const double slope) { cv_converter* conv; if (slope == 1) { conv = &trivialConverter; } else { conv = malloc(sizeof(ScaleConverter)); if (conv != NULL) { conv->ops = &scaleOps; conv->scale.value = slope; } } return conv; } /* * Returns a converter that adds a number to values (i.e., y = x + b). * When finished with the converter, the client should pass the converter to * cv_free(). * * Arguments: * offset The number to be added. * Returns: * NULL Necessary memory couldn't be allocated. * else A converter that adds the given number to values. */ cv_converter* cv_get_offset( const double offset) { cv_converter* conv; if (offset == 0) { conv = &trivialConverter; } else { conv = malloc(sizeof(OffsetConverter)); if (conv != NULL) { conv->ops = &offsetOps; conv->offset.value = offset; } } return conv; } /* * Returns a Galilean converter (i.e., y = ax + b). * When finished with the converter, the client should pass the converter to * cv_free(). * * Arguments: * slope The number by which to multiply values. * offset The number to be added. * Returns: * NULL Necessary memory couldn't be allocated. * else A Galilean converter corresponding to the inputs. */ cv_converter* cv_get_galilean( const double slope, const double intercept) { cv_converter* conv; if (slope == 1) { conv = cv_get_offset(intercept); } else if (intercept == 0) { conv = cv_get_scale(slope); } else { cv_converter* tmp = malloc(sizeof(GalileanConverter)); if (tmp != NULL) { tmp->ops = &galileanOps; tmp->galilean.slope = slope; tmp->galilean.intercept = intercept; conv = tmp; } } return conv; } /* * Returns a logarithmic converter (i.e., y = log(x/x0) in some base). * When finished with the converter, the client should pass the converter to * cv_free(). * * Arguments: * base The logarithmic base (e.g., 2, M_E, 10). Must be * greater than one. * Returns: * NULL "base" is invalid or necessary memory couldn't be * allocated. * else A logarithmic converter corresponding to the inputs. */ cv_converter* cv_get_log( const double base) { cv_converter* conv; if (base <= 1) { conv = NULL; } else { conv = malloc(sizeof(LogConverter)); if (conv != NULL) { conv->ops = &logOps; conv->log.logE = base == 2 ? M_LOG2E : base == M_E ? 1 : base == 10 ? M_LOG10E : 1/log(base); } } return conv; } /* * Returns an exponential converter (i.e., y = pow(b, x) in some base "b"). * When finished with the converter, the client should pass the converter to * cv_free(). * * Arguments: * base The desired base. Must be positive. * Returns: * NULL "base" is invalid or necessary memory couldn't be * allocated. * else An exponential converter corresponding to the inputs. */ cv_converter* cv_get_pow( const double base) { cv_converter* conv; if (base <= 0) { conv = NULL; } else { conv = malloc(sizeof(ExpConverter)); if (conv != NULL) { conv->ops = &expOps; conv->exp.base = base; } } return conv; } /* * Returns a converter corresponding to the sequential application of two * other converters. The returned converter should be passed to cv_free() when * it is no longer needed. * * Arguments: * first The converter to be applied first. May be passed to cv_free() * upon return. * second The converter to be applied second. May be passed to cv_free() * upon return. * Returns: * NULL Either "first" or "second" is NULL or necessary memory couldn't * be allocated. * else A converter corresponding to the sequential application of the * given converters. If one of the input converters is the trivial * converter, then the returned converter will be the other input * converter. */ cv_converter* cv_combine( cv_converter* const first, cv_converter* const second) { cv_converter* conv; if (first == NULL || second == NULL) { conv = NULL; } else if (IS_TRIVIAL(first)) { conv = CV_CLONE(second); } else if (IS_TRIVIAL(second)) { conv = CV_CLONE(first); } else { conv = NULL; if (IS_RECIPROCAL(first)) { if (IS_RECIPROCAL(second)) { conv = cv_get_trivial(); } } else if (IS_SCALE(first)) { if (IS_SCALE(second)) { conv = cv_get_scale(first->scale.value * second->scale.value); } else if (IS_OFFSET(second)) { conv = cv_get_galilean(first->scale.value, second->offset.value); } else if (IS_GALILEAN(second)) { conv = cv_get_galilean( first->scale.value * second->galilean.slope, second->galilean.intercept); } } else if (IS_OFFSET(first)) { if (IS_SCALE(second)) { conv = cv_get_galilean(second->scale.value, first->offset.value * second->scale.value); } else if (IS_OFFSET(second)) { conv = cv_get_offset(first->offset.value + second->offset.value); } else if (IS_GALILEAN(second)) { conv = cv_get_galilean(second->galilean.slope, first->offset.value * second->galilean.slope + second->galilean.intercept); } } else if (IS_GALILEAN(first)) { if (IS_SCALE(second)) { conv = cv_get_galilean( second->scale.value * first->galilean.slope, second->scale.value * first->galilean.intercept); } else if (IS_OFFSET(second)) { conv = cv_get_galilean(first->galilean.slope, first->galilean.intercept + second->offset.value); } else if (IS_GALILEAN(second)) { conv = cv_get_galilean( second->galilean.slope * first->galilean.slope, second->galilean.slope * first->galilean.intercept + second->galilean.intercept); } } if (conv == NULL) { /* * General case: create a composite converter. */ cv_converter* c1 = CV_CLONE(first); int error = 1; if (c1 != NULL) { cv_converter* c2 = CV_CLONE(second); if (c2 != NULL) { conv = malloc(sizeof(CompositeConverter)); if (conv != NULL) { conv->composite.ops = &compositeOps; conv->composite.first = c1; conv->composite.second = c2; error = 0; } /* "conv" allocated */ if (error) cv_free(c2); } /* "c2" allocated */ if (error) cv_free(c1); } /* "c1" allocated */ } /* "conv != NULL" */ } /* "first" & "second" not trivial */ return conv; } /* * Frees resources associated with a converter. Use of the converter argument * subsequent to this function may result in undefined behavior. * * Arguments: * conv The converter to have its resources freed or NULL. The * converter must have been returned by this module. */ void cv_free( cv_converter* const conv) { if (conv != NULL) { conv->ops->free((cv_converter*)conv); } } /* * Converts a float. * * Arguments: * converter Pointer to the converter. * value The value to be converted. * Returns: * The converted value. */ float cv_convert_float( const cv_converter* converter, const float value) { return (float)converter->ops->convertDouble(converter, value); } /* * Converts a double. * * Arguments: * converter Pointer to the converter. * value The value to be converted. * Returns: * The converted value. */ double cv_convert_double( const cv_converter* converter, const double value) { return converter->ops->convertDouble(converter, value); } /* * Converts an array of floats. * * Arguments: * converter Pointer to the converter. * in Pointer to the values to be converted. The array may * overlap "out". * count The number of values to be converted. * out Pointer to the output array for the converted values. * The array may overlap "in". * Returns: * NULL "converter", "in", or "out" is NULL. * else Pointer to the output array, "out". */ float* cv_convert_floats( const cv_converter* converter, const float* const in, const size_t count, float* out) { if (converter == NULL || in == NULL || out == NULL) { out = NULL; } else { out = converter->ops->convertFloats(converter, in, count, out); } return out; } /* * Converts an array of doubles. * * Arguments: * converter Pointer to the converter. * in Pointer to the values to be converted. The array may * overlap "out". * count The number of values to be converted. * out Pointer to the output array for the converted values. * The array may overlap "in". * Returns: * NULL "converter", "in", or "out" is NULL. * else Pointer to the output array, "out". */ double* cv_convert_doubles( const cv_converter* converter, const double* const in, const size_t count, double* out) { if (converter == NULL || in == NULL || out == NULL) { out = NULL; } else { out = converter->ops->convertDoubles(converter, in, count, out); } return out; } /* * Returns a string expression representation of a converter. * * Arguments: * conv The converter. * buf The buffer into which to write the expression. * max The size of the buffer. * variable The string to be used as the input value for the * converter. * RETURNS * <0 An error was encountered. * else The number of bytes formatted excluding the terminating null. */ int cv_get_expression( const cv_converter* const conv, char* const buf, size_t max, const char* const variable) { return conv->ops->getExpression(conv, buf, max, variable); }