Rev 44218 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/******************************************************************************** BDX: Binary Data eXchange format library* Copyright (C) 1999-2006 Thomas Baier** This library is free software; you can redistribute it and/or* modify it under the terms of the GNU Library General Public* License as published by the Free Software Foundation; either* version 2 of the License, or (at your option) any later version.** This library is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU* Library General Public License for more details.** You should have received a copy of the GNU Library General Public* License along with this library; if not, a copy is available at* http://www.r-project.org/Licenses/** Conversion functions from SEXP to BDX and vice versa.*******************************************************************************/#ifndef _BDX_SEXP_H_#include "bdx_SEXP.h"#endif#include "bdx.h"#include "bdx_util.h"#include "Rinternals.h"/*#include "rproxy_impl.h" */#include "com_util.h"#include <stdio.h>#include <assert.h>/** prototypes for internal helper functions*/static int EXTPTRSXP2LPSTREAM(RCOM_OBJHANDLE pHandle,LPSTREAM* pStream);#define getSpecialValueFromLogical(x) getSpecialValueFromInteger((x))static unsigned long getSpecialValueFromInteger(int pSEXPVal);static unsigned long getSpecialValueFromDouble(double pSEXPVal);static double getDoubleFromSpecialValue(unsigned long pSpecialValue);#define BDX_DM_DEFAULT 0UL#define BDX_DM_UNDEFINED ((unsigned long) -1)#define BDX_DM_NO_SPECIAL 1ULstatic unsigned long s_data_mode = BDX_DM_UNDEFINED;/* ==========================================================================Implementation========================================================================== *//* 05-05-19 | baier | handle BDX_GENERIC */int BDX2SEXP(BDX_Data const* pBDXData,SEXP* pSEXPData){SEXP lData = NULL;SEXP lDimensions = NULL;int lTotalSize = 1;int lProtectCount = 0;int i;assert(pSEXPData != NULL);switch(pBDXData->type & BDX_CMASK) {case BDX_SCALAR:/* no DIM attribute */break;case BDX_ARRAY:/* create DIM attribute */PROTECT(lDimensions = allocVector(INTSXP,pBDXData->dim_count));lProtectCount++;for (i = 0;i < pBDXData->dim_count;i++) {INTEGER (lDimensions)[i] = pBDXData->dimensions[i];lTotalSize *= pBDXData->dimensions[i];}break;default:/* unknown/unsupported type */UNPROTECT(lProtectCount);return -1;}/* now allocate the SEXP and copy the data */switch(pBDXData->type & BDX_SMASK) {case BDX_BOOL:lData = PROTECT(allocVector(LGLSXP,lTotalSize));lProtectCount++;for(i = 0;i < lTotalSize;i++) {LOGICAL(lData)[i] = pBDXData->data.raw_data[i].bool_value;}break;case BDX_INT:lData = PROTECT(allocVector(INTSXP,lTotalSize));lProtectCount++;for(i = 0;i < lTotalSize;i++) {INTEGER(lData)[i] = pBDXData->data.raw_data[i].int_value;}break;case BDX_DOUBLE:lData = PROTECT(allocVector(REALSXP,lTotalSize));lProtectCount++;for(i = 0;i < lTotalSize;i++) {REAL(lData)[i] = pBDXData->data.raw_data[i].double_value;}break;case BDX_STRING:lData = PROTECT(allocVector(STRSXP,lTotalSize));lProtectCount++;for(i = 0;i < lTotalSize;i++)SET_STRING_ELT(lData, i, mkChar(pBDXData->data.raw_data[i].string_value));break;case BDX_POINTER:/* BDX_POINTER not supported now, trace the contents */BDX_ERR(printf("BDX_POINTER found, value is %p\n",pBDXData->data.raw_data[0].ptr));*pSEXPData = NULL;UNPROTECT(lProtectCount);return -1;case BDX_HANDLE:/* only single COM object supported (no arrays) */if((pBDXData->type & BDX_CMASK) != BDX_SCALAR) {BDX_ERR(printf("array of BDX_HANDLE found. Not supported\n"));*pSEXPData = NULL;UNPROTECT(lProtectCount);return -1;}/* marshalled COM object as an LPSTREAM:** 1. unmarshal the object from the stream (IDispatch) using* CoGetInterfaceAndReleaseStream()* 2. COM object must be Release()d afterwards* 3. Stream still valid. Must be released afterwards*/{LPSTREAM lStream = pBDXData->data.raw_data[0].ptr;LPDISPATCH lDispatch;RCOM_OBJHANDLE lHandle;#if 1HRESULT lRc = CoUnmarshalInterface(lStream,&IID_IDispatch,(void*) &lDispatch);#elseHRESULT lRc = CoGetInterfaceAndReleaseStream(lStream,&IID_IDispatch,(void*) &lDispatch);#endifif(FAILED(lRc)) {BDX_ERR(printf("unmarshalling stream ptr %p failed with hr=%08x\n",lStream,lRc));*pSEXPData = NULL;UNPROTECT(lProtectCount);return -1;}/* create SEXP for COM object */lHandle = com_addObject (lDispatch);lData = com_createSEXP(lHandle);}break;case BDX_SPECIAL:lData = PROTECT (allocVector (REALSXP,lTotalSize));lProtectCount++;for(i = 0;i < lTotalSize;i++) {REAL(lData)[i] =getDoubleFromSpecialValue(pBDXData->data.raw_data[i].special_value);}break;case BDX_GENERIC:/* generic array */lData = PROTECT(allocVector(VECSXP,lTotalSize));lProtectCount++;for(i = 0;i < lTotalSize;i++) {SEXP lSEXP = NULL;/* handle contents */switch(pBDXData->data.raw_data_with_type[i].type & BDX_SMASK) {case BDX_BOOL:lSEXP = allocVector(LGLSXP,1);LOGICAL(lSEXP)[0] =pBDXData->data.raw_data_with_type[i].raw_data.bool_value;break;case BDX_INT:lSEXP = allocVector(INTSXP,1);INTEGER(lSEXP)[0] =pBDXData->data.raw_data_with_type[i].raw_data.int_value;break;case BDX_DOUBLE:lSEXP = allocVector(REALSXP,1);REAL(lSEXP)[0] =pBDXData->data.raw_data_with_type[i].raw_data.double_value;break;case BDX_STRING:lSEXP = mkString(pBDXData->data.raw_data_with_type[i].raw_data.string_value);break;case BDX_SPECIAL:lSEXP = allocVector(REALSXP,1);REAL(lSEXP)[0] =getDoubleFromSpecialValue(pBDXData->data.raw_data_with_type[i].raw_data.special_value);break;default:lSEXP = allocVector(REALSXP,1);REAL(lSEXP)[0] =getDoubleFromSpecialValue(BDX_SV_NULL);BDX_ERR(printf("unknown BDX type %d in generic vector element %d, using NA\n",pBDXData->data.raw_data_with_type[i].type & BDX_SMASK,i));}SET_VECTOR_ELT(lData,i,lSEXP);}break;default:/* unknown/unsupported BDX type */*pSEXPData = NULL;BDX_ERR(printf("unknown BDX type %08x, (SMASK %08x)\n",pBDXData->type,pBDXData->type & BDX_SMASK));UNPROTECT(lProtectCount);return -1;}if(lDimensions) {setAttrib (lData,R_DimSymbol,lDimensions);}*pSEXPData = lData;UNPROTECT(lProtectCount);return 0;}/* 05-05-24 | baier | VECSXP *//* 05-06-05 | baier | support for special values (R_NaN,...), generic vectors *//* 05-06-08 | baier | BDX_SPECIAL for scalars (REALSXP conversions) *//* 06-02-15 | baier | fixes for COM objects/EXTPTRSXP *//* 06-06-18 | baier | special values also for LOGICAL and INTEGER */int SEXP2BDX(struct SEXPREC const* pSexp,BDX_Data** ppBDXData){BDX_Data* lData;SEXP lDimension;int lTotalSize = 1;int i;int lGeneric = 0;SEXP sexp = (SEXP) pSexp; /* to get rid of const/non-const warning */double lDoubleValue;int lLogicalValue;int lIntegerValue;assert(ppBDXData != NULL);*ppBDXData = 0;lData = bdx_alloc();/** we support the following types at the moment** integer (scalar, vectors and arrays)* real (scalars, vectors and arrays)* logical (scalars, vectors and arrays)* string (scalars, vectors and arrays)* COM objects (IDispatch)* null** we should support soon** complex vectors* generic vectors* bug: no dimensions stored*//** zero-length SEXP: this is an error!*/if (LENGTH(sexp) == 0) {/* empty/undefined symbol */BDX_ERR(printf("SEXP2BDX: SEXP has length 0\n"));bdx_free(lData);return -6;}/** find out if the data type is supported*/switch(TYPEOF(sexp)) {case NILSXP:lData->type |= BDX_SPECIAL;break;case LGLSXP:lData->type |= BDX_BOOL;break;case INTSXP:lData->type |= BDX_INT;break;case REALSXP:lData->type |= BDX_DOUBLE;break;case EXTPTRSXP:lData->type |= BDX_HANDLE;break;case STRSXP:lData->type |= BDX_STRING;break;case VECSXP:/* generic vectors */lData->type |= BDX_GENERIC;lGeneric = 1;break;default:bdx_free(lData);BDX_TRACE(printf("SEXP2BDX: unsupported SEXP type %d\n",TYPEOF(sexp)));return -6;}/** vector/matrix/scalar*/lDimension = getAttrib (sexp,R_DimSymbol);PROTECT (lDimension);/* get dimension infos, allocate BDX_Dimension[] array */if(TYPEOF(lDimension) == INTSXP) {/* DIM atribute -> matrix */lData->type |= BDX_ARRAY;lData->dim_count = LENGTH(lDimension);lData->dimensions = (BDX_Dimension*) calloc(lData->dim_count,sizeof(BDX_Dimension));for (i = 0;i < lData->dim_count;i++) {lData->dimensions[i] = INTEGER(lDimension)[i];lTotalSize *= lData->dimensions[i];}/* lData->data.raw_data = (BDX_RawData*) malloc(sizeof(BDX_RawData)); */} else if((LENGTH(sexp) == 1)|| (TYPEOF(sexp) == EXTPTRSXP)){/* scalar */lData->type |= BDX_SCALAR;lData->dim_count = 1;lData->dimensions = (BDX_Dimension*) malloc(sizeof(BDX_Dimension));lData->dimensions[0] = 1;} else if(TYPEOF(lDimension) == NILSXP) {/* no DIM atribute -> vector (== one-dimensional array) */lData->type |= BDX_ARRAY;lData->dim_count = 1;lData->dimensions = (BDX_Dimension*) malloc(sizeof(BDX_Dimension));lData->dimensions[0] = LENGTH(sexp);lTotalSize = lData->dimensions[0];}UNPROTECT(1); /* unlock lDimension */if(!lGeneric) {/* allocate memory for the data: BDX_RawData or BDX_RawDataWithType */lData->data.raw_data = (BDX_RawData*) calloc(lTotalSize,sizeof(BDX_RawData));for(i = 0;(i < lTotalSize) && !lGeneric;i++) {switch(TYPEOF(sexp)) {case NILSXP:lData->data.raw_data[i].special_value = BDX_SV_NULL;break;case LGLSXP:/* check special values: R_NaInt */lLogicalValue = LOGICAL(sexp)[i];if(getSpecialValueFromLogical(lLogicalValue) != BDX_SV_UNK) {lGeneric = 1;} else {lData->data.raw_data[i].bool_value = lLogicalValue;}break;case INTSXP:/* check special values: R_NaInt */lIntegerValue = INTEGER(sexp)[i];if(getSpecialValueFromInteger(lIntegerValue) != BDX_SV_UNK) {lGeneric = 1;} else {lData->data.raw_data[i].int_value = lIntegerValue;}break;case REALSXP:/* check special values: R_NaReal, R_PosInf, R_NaN, R_NegInf */lDoubleValue = REAL(sexp)[i];if(getSpecialValueFromDouble(lDoubleValue) != BDX_SV_UNK) {lGeneric = 1;} else {lData->data.raw_data[i].double_value = lDoubleValue;}break;case EXTPTRSXP:/** according to BDR (mail on r-devel, 05-10-24) an EXTPTRSXP is not* a vector, therefore LENGTH() does not work*/lTotalSize = 1;#if 0/* no EXTPTRSXP for arrays */if(lTotalSize != 1) {bdx_free(lData);BDX_TRACE(printf("SEXP2BDX[1]: EXTPTRSXP in array with %d elements\n",lTotalSize));return -7;}#endif{/* is it a COM object? */RCOM_OBJHANDLE lHandle = com_getHandle(sexp);LPSTREAM lStream = NULL;int lRc = EXTPTRSXP2LPSTREAM(lHandle,&lStream); /* 0 for success *//* COM object is marshalled into stream** 1. stream object holds reference to IDispatch* 2. reference count is increased in the meanwhile* 3. must use CoGetInterfaceAndReleaseStream() to unmarshal* 4. Release() must be called on object afterwards*/if (lRc == 0) {/* we don't support generic pointers */lData->data.raw_data[0].ptr = lStream;lData->type |= BDX_HANDLE;#if 0if(lStream) {/* marshalled COM object */} else {/* pointer */lData->type |= BDX_POINTER;lData->data.raw_data[0].ptr =(unsigned long) R_ExternalPtrAddr(sexp);}#endif} else {lData->type |= BDX_POINTER;lData->data.raw_data[0].ptr = NULL; /* NULL pointer */BDX_TRACE(printf("SEXP2BDX: error %d marshalling COM object at index %d\n",lRc,i));}BDX_TRACE(printf("SEXP2BDX: base=%08x, type=%08x, ptr=%08x\n",lData,lData->type,lData->data.raw_data[0].ptr));}break;case STRSXP:lData->data.raw_data[i].string_value = strdup(CHAR(STRING_ELT(sexp,i)));if(lData->data.raw_data[i].string_value == NULL) {lData->data.raw_data[i].string_value = strdup("");}break;}}}/* VECSXP or REALSXP with special values */if(lGeneric) {if(lData->data.raw_data) {/* memory has been allocated */free(lData->data.raw_data);}if((lData->type & BDX_CMASK) == BDX_SCALAR) {/* scalar BDX_SPECIAL */lData->type = (lData->type & ~BDX_SMASK) | BDX_SPECIAL;lData->data.raw_data[0].special_value =getSpecialValueFromDouble(REAL(sexp)[0]);} else {/* BDX_GENERIC */lData->type = (lData->type & ~BDX_SMASK) | BDX_GENERIC;lData->data.raw_data_with_type =(BDX_RawDataWithType*) calloc(lTotalSize,sizeof(BDX_RawDataWithType));switch(TYPEOF(sexp)) {case LGLSXP:for(i = 0;i < lTotalSize;i++) {lData->data.raw_data_with_type[i].type = BDX_SPECIAL;lData->data.raw_data_with_type[i].raw_data.special_value =getSpecialValueFromLogical(LOGICAL(sexp)[i]);if(lData->data.raw_data_with_type[i].raw_data.special_value ==BDX_SV_UNK) {lData->data.raw_data_with_type[i].type = BDX_BOOL;lData->data.raw_data_with_type[i].raw_data.double_value =LOGICAL(sexp)[i];}}break;case INTSXP:for(i = 0;i < lTotalSize;i++) {lData->data.raw_data_with_type[i].type = BDX_SPECIAL;lData->data.raw_data_with_type[i].raw_data.special_value =getSpecialValueFromInteger(INTEGER(sexp)[i]);if(lData->data.raw_data_with_type[i].raw_data.special_value ==BDX_SV_UNK) {lData->data.raw_data_with_type[i].type = BDX_INT;lData->data.raw_data_with_type[i].raw_data.double_value =LOGICAL(sexp)[i];}}break;case REALSXP:for(i = 0;i < lTotalSize;i++) {lData->data.raw_data_with_type[i].type = BDX_SPECIAL;lData->data.raw_data_with_type[i].raw_data.special_value =getSpecialValueFromDouble(REAL(sexp)[i]);if(lData->data.raw_data_with_type[i].raw_data.special_value ==BDX_SV_UNK) {lData->data.raw_data_with_type[i].type = BDX_DOUBLE;lData->data.raw_data_with_type[i].raw_data.double_value =REAL(sexp)[i];}}break;default:for(i = 0;i < lTotalSize;i++) {SEXP lElementSexp = VECTOR_ELT(sexp,i);switch(TYPEOF(lElementSexp)) {case NILSXP:lData->data.raw_data_with_type[i].type = BDX_SPECIAL;lData->data.raw_data_with_type[i].raw_data.special_value = BDX_SV_NULL;break;case LGLSXP:lData->data.raw_data_with_type[i].type = BDX_SPECIAL;lData->data.raw_data_with_type[i].raw_data.special_value =getSpecialValueFromLogical(LOGICAL(lElementSexp)[0]);if(lData->data.raw_data_with_type[i].raw_data.special_value == BDX_SV_UNK) {lData->data.raw_data_with_type[i].type = BDX_BOOL;lData->data.raw_data_with_type[i].raw_data.bool_value =LOGICAL(lElementSexp)[0];}break;case INTSXP:lData->data.raw_data_with_type[i].type = BDX_SPECIAL;lData->data.raw_data_with_type[i].raw_data.special_value =getSpecialValueFromInteger(INTEGER(lElementSexp)[0]);if(lData->data.raw_data_with_type[i].raw_data.special_value == BDX_SV_UNK) {lData->data.raw_data_with_type[i].type = BDX_INT;lData->data.raw_data_with_type[i].raw_data.int_value =INTEGER(lElementSexp)[0];}break;case REALSXP:lData->data.raw_data_with_type[i].type = BDX_SPECIAL;lData->data.raw_data_with_type[i].raw_data.special_value =getSpecialValueFromDouble(REAL(lElementSexp)[0]);if(lData->data.raw_data_with_type[i].raw_data.special_value == BDX_SV_UNK) {lData->data.raw_data_with_type[i].type = BDX_DOUBLE;lData->data.raw_data_with_type[i].raw_data.double_value =REAL(lElementSexp)[0];}break;case EXTPTRSXP:lData->data.raw_data_with_type[i].type = BDX_HANDLE;/** according to BDR (mail on r-devel, 05-10-24) an EXTPTRSXP is not* a vector, therefore LENGTH() does not work*/lTotalSize = 1;/* no EXTPTRSXP for arrays */{/* is it a COM object? */RCOM_OBJHANDLE lHandle = com_getHandle(lElementSexp);LPSTREAM lStream = NULL;int lRc = EXTPTRSXP2LPSTREAM(lHandle,&lStream); /* 0 for success *//* COM object is marshalled into stream** 1. stream object holds reference to IDispatch* 2. reference count is increased in the meanwhile* 3. must use CoGetInterfaceAndReleaseStream() to unmarshal* 4. Release() must be called on object afterwards*/if (lRc == 0) {/* we don't support generic pointers */lData->data.raw_data_with_type[i].raw_data.ptr = lStream;lData->type |= BDX_HANDLE;} else {lData->type |= BDX_POINTER;lData->data.raw_data_with_type[i].raw_data.ptr =NULL; /* NULL pointer */BDX_TRACE(printf("SEXP2BDX: error %d marshalling COM object at index %d\n",lRc,i));}}break;case STRSXP:lData->data.raw_data_with_type[i].type = BDX_STRING;lData->data.raw_data_with_type[i].raw_data.string_value =strdup(CHAR(STRING_ELT(lElementSexp,0)));break;default:bdx_free(lData);BDX_TRACE(printf("SEXP2BDX: unsupported SEXP type %d in VECSXP element %d\n",TYPEOF(lElementSexp),i));return -6;}}}}}*ppBDXData = lData;return 0;}unsigned long bdx_get_datamode(void){return s_data_mode;}void bdx_set_datamode(unsigned long pDM){BDX_TRACE(printf("BDX: data mode set to %08x\n",pDM));s_data_mode = pDM;}static int EXTPTRSXP2LPSTREAM(RCOM_OBJHANDLE pHandle,LPSTREAM* pStream){IUnknown* lUnk = NULL;LPSTREAM lStream = NULL;HRESULT hr = E_FAIL;if (pHandle == RCOM_NULLHANDLE) {return -1;}/* valid COM object */lUnk = (LPUNKNOWN) com_getObject(pHandle);/** Marshal interface into stream (can be unmarshalled in any thread of* the same process. This may slow down things a bit, but we're on the* safe side if the BDX_Data is processed in a thread different from* the current one.*/hr = CoMarshalInterThreadInterfaceInStream(&IID_IUnknown,lUnk,&lStream);if(FAILED(hr)) {BDX_TRACE(printf("SEXP2BDX: error %08x marshalling interface into stream\n",hr));return -5;} else {*pStream = lStream;}return 0;}/* 06-06-24 | baier | NaN also transforms to IEEE double NaN for dm=1 */static unsigned long getSpecialValueFromDouble(double pSEXPVal){if(ISNA(pSEXPVal)) {return BDX_SV_NA;} else if(pSEXPVal == R_PosInf) {if(bdx_get_datamode() == BDX_DM_NO_SPECIAL) {return BDX_SV_UNK;}return BDX_SV_INF;} else if(ISNAN(pSEXPVal)) {if(bdx_get_datamode() == BDX_DM_NO_SPECIAL) {return BDX_SV_UNK;}return BDX_SV_NAN;} else if(pSEXPVal == R_NegInf) {if(bdx_get_datamode() == BDX_DM_NO_SPECIAL) {return BDX_SV_UNK;}return BDX_SV_NINF;}return BDX_SV_UNK;}static unsigned long getSpecialValueFromInteger(int pSEXPVal){if(pSEXPVal == R_NaInt) {return BDX_SV_NA;}return BDX_SV_UNK;}static double getDoubleFromSpecialValue(unsigned long pSpecialValue){switch(pSpecialValue) {case BDX_SV_NULL:case BDX_SV_NA:/* NULL and NA both transform to NA */return R_NaReal;case BDX_SV_DIV0:case BDX_SV_INF:/* DIV/0 and +Inf transform to +Inf */return R_PosInf;case BDX_SV_NAN:/* NAN is just NaN */return R_NaN;case BDX_SV_NINF:/* -Inf is -Inf */return R_NegInf;case BDX_SV_UNK:default:return R_NaReal;}}