/********************************************************************* * Copyright 2018, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. *********************************************************************/ #include "config.h" #include "dapincludes.h" #ifdef _WIN32 #include #include #endif struct Value { long long llval; double dval; }; /*Forward*/ static int cvtnumconst(const char* s, struct Value* val); static int cvtdbl2int(struct Value* val); static int cvtint2dbl(struct Value* val); static int cvtint2int(nc_type dsttype, struct Value*); NCerror dapconvert(nc_type srctype, nc_type dsttype, char* memory0, char* value0, size_t count) { NCerror ncstat = NC_NOERR; size_t i; char* memory = memory0; char* value = value0; /* In order to deal with the DAP upgrade problem, try to preserve the bit patterns */ /* Provide space and pointer casts for intermediate results */ signed char ncbyte; signed char* ncbytep; char ncchar; char* nccharp; short ncshort; short* ncshortp; int ncint; int* ncintp; float ncfloat; float* ncfloatp; double ncdouble; double* ncdoublep; unsigned char ncubyte; unsigned char* ncubytep; unsigned short ncushort; unsigned short* ncushortp; unsigned int ncuint; unsigned int* ncuintp; long long ncint64; long long* ncint64p; unsigned long long ncuint64; unsigned long long* ncuint64p; #define CASE(nc1,nc2) (nc1*256+nc2) #define CUT8(e) ((unsigned char)((e) & 0xff)) #define CUT16(e) ((unsigned short)((e) & 0xffff)) #define CUT32(e) ((unsigned int)((e) & 0xffffffff)) #define ARM(vs,ncs,ts,vd,ncd,td) \ case CASE(ncs,ncd):\ vs##p = (ts *)value;\ vs = *vs##p;\ vd##p = (td *)memory;\ *vd##p = (td)vs;\ break; for(i=0;iname,s); ncstat = NC_EBADTYPE; goto next; } /* Force conformance with etype */ /* Do Specific conversions */ if(stype == NC_DOUBLE && etype < NC_FLOAT) {/* double->integer-type */ if((ncstat = cvtdbl2int(&val))) goto next; stype = NC_INT; /* pretend */ }else if(stype == NC_INT && etype >= NC_FLOAT) {/*integer-type -> float type*/ if((ncstat = cvtint2dbl(&val))) goto next; stype = NC_DOUBLE; /* pretend */ } if(stype == NC_INT && etype < NC_FLOAT) {/* integer-type -> integer-type */ if((ncstat = cvtint2int(etype,&val))) goto next; } switch (etype) { case NC_BYTE: /* Note that in DAP2, this is unsigned 8-bit integer */ u8p = (unsigned char*)dstmem; *u8p = (unsigned char)(val.llval); break; case NC_SHORT: i16p = (short*)dstmem; *i16p = (short)(val.llval); break; case NC_USHORT: u16p = (unsigned short*)dstmem; *u16p = (unsigned short)(val.llval); break; case NC_INT: i32p = (int*)dstmem; *i32p = (int)(val.llval); break; case NC_UINT: u32p = (unsigned int*)dstmem; *u32p = (unsigned int)(val.llval); break; case NC_FLOAT: fp = (float*)dstmem; *fp = (float)(val.dval); break; case NC_DOUBLE: dp = (double*)dstmem; *dp = (double)(val.dval); break; default: return NC_EINTERNAL; } } else if(etype == NC_CHAR) { char* p = (char*)dstmem; size_t count; int nread; count = sscanf(s,"%c%n",p,&nread); if(count != 1 || nread != slen) {ncstat = NC_EBADTYPE; goto next;} } else if(etype == NC_STRING || etype == NC_URL) { char** p = (char**)dstmem; *p = nulldup(s); } else { PANIC1("unexpected nc_type: %d",(int)etype); } next: /* inside loop */ if(ncstat == NC_ERANGE) nclog(NCLOGERR,"Attribute value out of range: %s = %s",att->name,s); else if(ncstat == NC_EBADTYPE) nclog(NCLOGERR,"Unexpected attribute type or untranslatable value: %s",att->name); ncstat = NC_NOERR; dstmem += memsize; } return THROW(ncstat); } /** @param val resulting converted numeric value @return NC_INT || NC_DOUBLE || NC_NAT (failure) */ static int cvtnumconst(const char* s, struct Value* val) { size_t slen = strlen(s); int nread; /* # of chars read */ size_t count; /* # of conversions */ /* Try to convert to integer first */ count = sscanf(s,"%lld%n",&val->llval,&nread); if(count == 1 && nread == slen) return NC_INT; /* Try to convert to float second */ #ifdef _WIN32 if (!_strnicmp(s, "NaN", 3)) {count = 1; nread = 3; val->dval = NAN;} else #endif count = sscanf(s,"%lg%n",&val->dval,&nread); if(count == 1 && nread == slen) return NC_DOUBLE; return NC_INT; } /** Convert a struct Value.dval field to a long in struct Value.llval field. Report if the result is out of range wrt NC_MAX/MIN_INT. @param val store original and converted value @return NC_NOERR | NC_ERANGE */ static int cvtdbl2int(struct Value* val) { /* Inter-convert */ #ifdef _WIN32 if(isnan(val->dval)) return NC_ERANGE; #endif val->llval = (long long)val->dval; if(val->llval < NC_MIN_INT || val->llval > NC_MAX_INT) return NC_ERANGE; return NC_NOERR; } /** Convert a struct Value.llval field to double in struct Value.dval field. @return NC_NOERR */ static int cvtint2dbl(struct Value* val) { /* Inter-convert */ val->dval = (double)val->llval; return NC_NOERR; } /** Convert long long bit pattern to conform to a given integer type. @param dsttype target integer type @param valp pointer to long long value to convert @return NC_NOERR | NC_EBADTYPE */ static int cvtint2int(nc_type dsttype, struct Value* val) { /* assert dsttype < NC_FLOAT && dsttype != NC_CHAR */ /* We do not actually do much in the way of range checking, we just truncate the bit pattern to the proper size, taking signedness into account */ switch (dsttype) { case NC_BYTE: val->llval = (long long)((signed char)(val->llval)); break; case NC_UBYTE: val->llval = (long long)(val->llval & 0xFF); break; case NC_SHORT: val->llval = (long long)((short)(val->llval)); break; case NC_USHORT: val->llval = (long long)(val->llval & 0xFFFF); break; case NC_INT: val->llval = (long long)((int)(val->llval)); break; case NC_UINT: val->llval = (long long)(val->llval & 0xFFFFFFFF); break; default: return NC_EBADTYPE; } return NC_NOERR; }