/** * Cases.c --- This file contains functions for working with case set files */ #define _GNU_SOURCE /* Needed for mempcpy */ #include #ifdef __APPLE__ /* Fails for Macs, need to define mempcpy explicitly */ /* From Gnulib */ void * mempcpy (void *dest, const void *src, size_t n) { return (char *) memcpy (dest, src, n) + n; } #endif #include #include #include /** * Case sets are generally tab separated files of variable values, * with each column representing a variable. Generally, we will want * to pass one of two things when Netica wants a caseset: (1) A file * name of a file containing a case set or (2) a data frame, which we * can write to a memory buffer and create a case set from that. */ SEXP RN_CaseFileDelimiter(SEXP newchar) { int result; if (isNull(newchar)) { result = SetCaseFileDelimChar_ns(QUERY_ns,RN_netica_env); } else { result = SetCaseFileDelimChar_ns(INTEGER(newchar)[0],RN_netica_env); } return ScalarInteger(result); } SEXP RN_MissingCode(SEXP newchar) { int result; if (isNull(newchar)) { result = SetMissingDataChar_ns(QUERY_ns,RN_netica_env); } else { result = SetMissingDataChar_ns(INTEGER(newchar)[0],RN_netica_env); } return ScalarInteger(result); } /** * Much of the next bit follows * http://www.stat.uiowa.edu/~luke/R/references/weakfinex.html * * This is not well documented in Luke's web page, but I've put the * stream pointer in as the "key" of the weak pointer list, and the * NeticaCaseStream object as the "value". This allows me to set the * pointer to NULL when I'm closing all open streams, say on detatch. */ /** * The Netica API uses file streams in four places: * 1) Reading networks * 2) Writing networks * 3) Reading Case files * 4) Writing Case files * Only in case 3 do we need to keep the stream open after returning * to R. In particular, that means we can stash information about the * current case position in the NeticaStream object. However, * allowing for case 4 as well allows us to support memory streams. */ /** * Finalizer: calling this multiple times should be harmless. */ SEXP CaseStreamClose (SEXP streamptr) { stream_ns *stream_handle; if (streamptr == NULL || isNull(streamptr)) { //Already closed, nothing to do } else { if (TYPEOF(streamptr) != EXTPTRSXP || R_ExternalPtrTag(streamptr) != casestreamatt) { warning("Trying to close a non-stream pointer"); } else { stream_handle = (stream_ns*) R_ExternalPtrAddr(streamptr); if (stream_handle != NULL) { DeleteStream_ns(stream_handle); R_ClearExternalPtr(streamptr); } } } return R_NilValue; } void AddStreamRef(SEXP ref) { SEXP s, streams, next=NULL, last=NULL; streams = CDR(CaseStreamList); for (s = streams; s != R_NilValue; s = next) { SEXP r = CAR(s); SEXP key = R_WeakRefKey(r); next = CDR(s); if (key == R_NilValue || R_ExternalPtrAddr(key)==NULL) { if (last == NULL) streams = next; else SETCDR(last,next); } else { last = s; } } SETCDR(CaseStreamList, CONS(ref,streams)); } void CloseOpenCaseStreams () { SEXP s, streams, next=NULL, last=NULL; streams = CDR(CaseStreamList); for (s = streams; s != R_NilValue; s = next) { SEXP r = CAR(s); SEXP key = R_WeakRefKey(r); SEXP stream = R_WeakRefValue(r); next = CDR(s); if (key != R_NilValue) { CaseStreamClose(key); if (stream && stream != R_NilValue) { setAttrib(stream,casestreamatt,R_NilValue); } } } } SEXP RN_isCaseStreamActive(SEXP stream) { SEXP stPtr, result; PROTECT(result=allocVector(LGLSXP,1)); LOGICAL(result)[0]=FALSE; PROTECT(stPtr = getAttrib(stream,casestreamatt)); if (!isNull(stPtr) && R_ExternalPtrAddr(stPtr)) { LOGICAL(result)[0] = TRUE; } UNPROTECT(2); return result; } SEXP RN_OpenCaseFileStream (SEXP path, SEXP stream) { const char* pathname=CHAR(STRING_ELT(path,0)); stream_ns* str = NewFileStream_ns (pathname,RN_netica_env, NULL); if (str == NULL ) return R_NilValue; else { SEXP stPtr, ref; if (isNull(stream)) { //Allocate new stream object PROTECT(stream = allocVector(STRSXP,1)); SET_STRING_ELT(stream,0,mkChar(pathname)); SET_CLASS(stream,casefilestreamclass); } else { PROTECT(stream); //To keep protect count constant } PROTECT(stPtr = R_MakeExternalPtr(str,casestreamatt, R_NilValue)); setAttrib(stream,casestreamatt,stPtr); PROTECT(ref = R_MakeWeakRefC(stPtr,stream, (R_CFinalizer_t) &CaseStreamClose, TRUE)); AddStreamRef(ref); setAttrib(stream,casestreampathatt,path); // Use pos of NULL to indicate start from the beginning. setAttrib(stream,casestreamposatt,R_NilValue); setAttrib(stream,casestreamlastidatt,R_NilValue); setAttrib(stream,casestreamlastfreqatt,R_NilValue); setAttrib(stream,casestreamdfatt,R_NilValue); setAttrib(stream,casestreamdfnameatt,R_NilValue); UNPROTECT(3); return stream; } } SEXP RN_OpenCaseMemoryStream (SEXP label, SEXP stream) { const char* lab=CHAR(STRING_ELT(label,0)); //Rprintf("Opening Stream for R object %s\n",lab); stream_ns* str = NewMemoryStream_ns (lab,RN_netica_env, NULL); if (str == NULL ) return R_NilValue; else { SEXP stPtr, ref; if (isNull(stream)) { //Allocate new stream object PROTECT(stream = allocVector(STRSXP,1)); SET_STRING_ELT(stream,0,mkChar(lab)); SET_CLASS(stream,memorystreamclass); } else { PROTECT(stream); //To keep protect count constant } PROTECT(stPtr = R_MakeExternalPtr(str,casestreamatt, R_NilValue)); setAttrib(stream,casestreamatt,stPtr); PROTECT(ref = R_MakeWeakRefC(stPtr,stream, (R_CFinalizer_t) &CaseStreamClose, TRUE)); AddStreamRef(ref); setAttrib(stream,casestreamdfnameatt,label); // Use pos of NULL to indicate start from the beginning. setAttrib(stream,casestreamposatt,R_NilValue); setAttrib(stream,casestreamlastidatt,R_NilValue); setAttrib(stream,casestreamlastfreqatt,R_NilValue); setAttrib(stream,casestreamdfatt,R_NilValue); setAttrib(stream,casestreampathatt,R_NilValue); UNPROTECT(3); return stream; } } SEXP RN_CloseCaseStream (SEXP stream) { if (!isNeticaStream(stream)) { warning("Trying to close a non-Stream object."); } CaseStreamClose(getAttrib(stream,casestreamatt)); setAttrib(stream,casestreamatt,R_NilValue); return(stream); } /** * Tests whether or not an object is a Netica stream */ int isNeticaStream(SEXP obj) { SEXP klass; int result = FALSE; PROTECT(klass = getAttrib(obj,R_ClassSymbol)); R_len_t k, kk=length(klass); for (k=0; k