Rev 6191 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R : A Computer Language for Statistical Data Analysis* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka* Copyright (C) 1997--1999 Robert Gentleman, Ross Ihaka and the* R Development Core Team** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program 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 General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#ifdef HAVE_CONFIG_H#include <Rconfig.h>#endif#include "Defn.h"#include "Mathlib.h"#include "Fileio.h"#ifdef HAVE_HDF5#include <hdf5.h>#endif/* Static Globals */static char smbuf[512]; /* Small buffer for temp use */static char *buf=NULL; /* Buffer for character strings */static char *bufp; /* A pointer to that buffer */static int bufsize=0; /* Current buffer size */static int NSymbol; /* Number of symbols */static int NSave; /* Number of non-symbols */static int NTotal; /* NSymbol + NSave */static int NVSize; /* Number of vector cells */static int *OldOffset; /* Offsets in previous incarnation */static SEXP *NewAddress; /* Addresses in this incarnation */static int VersionId;static int DLstartup; /* Allows different error action on startup */static SEXP DataLoad(FILE*);static void DataSave(SEXP, FILE*);static void AllocBuffer(int len){if(len >= 0 ) {if(len*sizeof(char) < bufsize) return;len = (len+1)*sizeof(char);if(len < MAXELTSIZE) len = MAXELTSIZE;buf = (char *) realloc(buf, len);bufsize = len;if(!buf) {bufsize = 0;error("Could not allocate memory for string save/load");}} else {if(bufsize == MAXELTSIZE) return;free(buf);buf = (char *) malloc(MAXELTSIZE);bufsize = MAXELTSIZE;}}/* I/O Function Pointers */static void (*OutInit)(FILE*);static void (*OutInteger)(FILE*, int);static void (*OutReal)(FILE*, double);static void (*OutComplex)(FILE*, complex);static void (*OutString)(FILE*, char*);static void (*OutSpace)(FILE*);static void (*OutNewline)(FILE*);static void (*OutTerm)(FILE*);static void (*InInit)(FILE*);static int (*InInteger)(FILE*);static double (*InReal)(FILE*);static complex (*InComplex)(FILE*);static char* (*InString)(FILE*);static void (*InTerm)(FILE*);/* Dummy Placeholder Routine */static void Dummy(FILE *fp){}/* Functions for Ascii Pickling */static void AsciiOutInteger(FILE *fp, int i){if (i == NA_INTEGER)fprintf(fp, "NA");elsefprintf(fp, "%d", i);}static int AsciiInInteger(FILE *fp){int x;fscanf(fp, "%s", smbuf);if (strcmp(smbuf, "NA") == 0)return NA_INTEGER;else {sscanf(smbuf, "%d", &x);return x;}}static void AsciiOutReal(FILE *fp, double x){if (!R_FINITE(x)) {if (ISNAN(x))fprintf(fp, "NA");else if (x < 0)fprintf(fp, "-Inf");elsefprintf(fp, "Inf");}else fprintf(fp, "%.16g", x);/* 16: full precision; 17 gives 999, 000 &c */}static double AsciiInReal(FILE *fp){double x;fscanf(fp, "%s", smbuf);if (strcmp(smbuf, "NA") == 0)x = NA_REAL;else if (strcmp(smbuf, "Inf") == 0)x = R_PosInf;else if (strcmp(smbuf, "-Inf") == 0)x = R_NegInf;elsesscanf(smbuf, "%lg", &x);return x;}static void AsciiOutComplex(FILE *fp, complex x){if (ISNAN(x.r) || ISNAN(x.i))fprintf(fp, "NA NA");elsefprintf(fp, "%g %g", x.r, x.i);}static complex AsciiInComplex(FILE *fp){complex x;fscanf(fp, "%s", smbuf);if (strcmp(smbuf, "NA") == 0)x.r = NA_REAL;else if (strcmp(smbuf, "Inf") == 0)x.r = R_PosInf;else if (strcmp(smbuf, "-Inf") == 0)x.r = R_NegInf;elsesscanf(smbuf, "%lg", &x.r);fscanf(fp, "%s", smbuf);if (strcmp(smbuf, "NA") == 0)x.i = NA_REAL;else if (strcmp(smbuf, "Inf") == 0)x.i = R_PosInf;else if (strcmp(smbuf, "-Inf") == 0)x.i = R_NegInf;elsesscanf(smbuf, "%lg", &x.i);return x;}static void AsciiOutSpace(FILE *fp){fputc(' ', fp);}static void AsciiOutNewline(FILE *fp){fputc('\n', fp);}/* FIXME : To make saved files completely portable, the output *//* representation of strings should be completely ascii. This *//* includes control characters and non-ascii characters. *//* This could be done with \ooo escapes. */static void AsciiOutString(FILE *fp, char *s){char *p = s;fputc('\"', fp);while (*p) {switch(*p) {case '\n': fputc('\\', fp); fputc('n', fp); break;case '\t': fputc('\\', fp); fputc('t', fp); break;case '\v': fputc('\\', fp); fputc('v', fp); break;case '\b': fputc('\\', fp); fputc('b', fp); break;case '\r': fputc('\\', fp); fputc('r', fp); break;case '\f': fputc('\\', fp); fputc('f', fp); break;case '\a': fputc('\\', fp); fputc('a', fp); break;case '\\': fputc('\\', fp); fputc('\\', fp); break;case '\?': fputc('\\', fp); fputc('\?', fp); break;case '\'': fputc('\\', fp); fputc('\'', fp); break;case '\"': fputc('\\', fp); fputc('\"', fp); break;default: fputc(*p, fp); break;}p++;}fputc('\"', fp);}static char *AsciiInString(FILE *fp){int c;bufp = buf;while ((c = R_fgetc(fp)) != '"');while ((c = R_fgetc(fp)) != R_EOF && c != '"') {if (c == '\\') {if ((c = R_fgetc(fp)) == R_EOF) break;switch(c) {case 'n': c = '\n'; break;case 't': c = '\t'; break;case 'v': c = '\v'; break;case 'b': c = '\b'; break;case 'r': c = '\r'; break;case 'f': c = '\f'; break;case 'a': c = '\a'; break;case '\\': c = '\\'; break;case '\?': c = '\?'; break;case '\'': c = '\''; break;case '\"': c = '\"'; break;default: break;}}*bufp++ = c;}*bufp = '\0';return buf;}void AsciiSave(SEXP s, FILE *fp){OutInit = Dummy;OutInteger = AsciiOutInteger;OutReal = AsciiOutReal;OutComplex = AsciiOutComplex;OutString = AsciiOutString;OutSpace = AsciiOutSpace;OutNewline = AsciiOutNewline;OutTerm = Dummy;DataSave(s, fp);}SEXP AsciiLoad(FILE *fp){VersionId = 0;InInit = Dummy;InInteger = AsciiInInteger;InReal = AsciiInReal;InComplex = AsciiInComplex;InString = AsciiInString;InTerm = Dummy;return DataLoad(fp);}SEXP AsciiLoadOld(FILE *fp, int version){VersionId = version;InInit = Dummy;InInteger = AsciiInInteger;InReal = AsciiInReal;InComplex = AsciiInComplex;InString = AsciiInString;InTerm = Dummy;return DataLoad(fp);}#ifdef HAVE_RPC_XDR_H/* Functions for Binary Pickling Using XDR */#include <rpc/rpc.h>XDR xdrs;static void XdrOutInit(FILE *fp){xdrstdio_create(&xdrs, fp, XDR_ENCODE);}static void XdrOutTerm(FILE *fp){xdr_destroy(&xdrs);}static void XdrInInit(FILE *fp){xdrstdio_create(&xdrs, fp, XDR_DECODE);}static void XdrInTerm(FILE *fp){xdr_destroy(&xdrs);}static void XdrOutInteger(FILE *fp, int i){if (!xdr_int(&xdrs, &i)) {xdr_destroy(&xdrs);error("a I write error occured");}}static int XdrInInteger(FILE * fp){int i;if (!xdr_int(&xdrs, &i)) {xdr_destroy(&xdrs);error("a Iread error occured");}return i;}static void XdrOutReal(FILE *fp, double x){if (!xdr_double(&xdrs, &x)) {xdr_destroy(&xdrs);error("a R write error occured");}}static double XdrInReal(FILE * fp){double x;if (!xdr_double(&xdrs, &x)) {xdr_destroy(&xdrs);error("a R read error occured");}return x;}static void XdrOutComplex(FILE *fp, complex x){if (!xdr_double(&xdrs, &(x.r)) || !xdr_double(&xdrs, &(x.i))) {xdr_destroy(&xdrs);error("a C write error occured");}}static complex XdrInComplex(FILE * fp){complex x;if (!xdr_double(&xdrs, &(x.r)) || !xdr_double(&xdrs, &(x.i))) {xdr_destroy(&xdrs);error("a C read error occured");}return x;}static void XdrOutString(FILE *fp, char *s){if (!xdr_string(&xdrs, &s, strlen(s))) {xdr_destroy(&xdrs);error("a S write error occured");}}static char *XdrInString(FILE *fp){char *bufp = buf;if (!xdr_string(&xdrs, &bufp, bufsize)) {xdr_destroy(&xdrs);error("a S read error occured");}return buf;}#ifdef EXPERIMENTAL#include "saveload-x.c"#endifstatic void XdrSave(SEXP s, FILE *fp){OutInit = XdrOutInit;OutInteger = XdrOutInteger;OutReal = XdrOutReal;OutComplex = XdrOutComplex;OutString = XdrOutString;OutSpace = Dummy;OutNewline = Dummy;OutTerm = XdrOutTerm;#ifdef EXPERIMENTALcky_DataSave(s, fp);#elseDataSave(s, fp);#endif}static SEXP XdrLoad(FILE *fp){VersionId = 0;InInit = XdrInInit;InInteger = XdrInInteger;InReal = XdrInReal;InComplex = XdrInComplex;InString = XdrInString;InTerm = XdrInTerm;#ifdef EXPERIMENTALreturn cky_DataLoad(fp);#elsereturn DataLoad(fp);#endif}#endif/* Functions for Binary Pickling */static void BinaryOutInteger(FILE *fp, int i){if (fwrite(&i, sizeof(int), 1, fp) != 1)error("a write error occured");}static int BinaryInInteger(FILE * fp){int i;if (fread(&i, sizeof(int), 1, fp) != 1)error("a read error occured");return i;}static void BinaryOutReal(FILE *fp, double x){if (fwrite(&x, sizeof(double), 1, fp) != 1)error("a write error occured");}static double BinaryInReal(FILE * fp){double x;if (fread(&x, sizeof(double), 1, fp) != 1)error("a read error occured");return x;}static void BinaryOutComplex(FILE *fp, complex x){if (fwrite(&x, sizeof(complex), 1, fp) != 1)error("a write error occured");}static complex BinaryInComplex(FILE * fp){complex x;if (fread(&x, sizeof(complex), 1, fp) != 1)error("a read error occured");return x;}static void BinaryOutString(FILE *fp, char *s){int n = strlen(s) + 1; /* NULL too */if (fwrite(s, sizeof(char), n, fp) != n)error("a write error occured");}static char *BinaryInString(FILE *fp){bufp = buf;do {*bufp = R_fgetc(fp);}while (*bufp++);return buf;}void BinarySave(SEXP s, FILE *fp){OutInit = Dummy;OutInteger = BinaryOutInteger;OutReal = BinaryOutReal;OutComplex = BinaryOutComplex;OutString = BinaryOutString;OutSpace = Dummy;OutNewline = Dummy;OutTerm = Dummy;DataSave(s, fp);}SEXP BinaryLoad(FILE *fp){VersionId = 0;InInit = Dummy;InInteger = BinaryInInteger;InReal = BinaryInReal;InComplex = BinaryInComplex;InString = BinaryInString;InTerm = Dummy;return DataLoad(fp);}SEXP BinaryLoadOld(FILE *fp, int version){VersionId = version;InInit = Dummy;InInteger = BinaryInInteger;InReal = BinaryInReal;InComplex = BinaryInComplex;InString = BinaryInString;InTerm = Dummy;return DataLoad(fp);}/* Magic Numbers for R Save File Types */void R_WriteMagic(FILE *fp, int number){unsigned char buf[5];number = abs(number);buf[0] = (number/1000) % 10 + '0';buf[1] = (number/100) % 10 + '0';buf[2] = (number/10) % 10 + '0';buf[3] = number % 10 + '0';buf[4] = '\n';fwrite((char*)buf, sizeof(char), 5, fp);}int R_ReadMagic(FILE *fp){unsigned char buf[6];int d1, d2, d3, d4, d1234;fread((char*)buf, sizeof(char), 5, fp);/* Intel gcc seems to screw up a single expression here */d1 = (buf[3]-'0') % 10;d2 = (buf[2]-'0') % 10;d3 = (buf[1]-'0') % 10;d4 = (buf[0]-'0') % 10;return d1234 = d1 + 10 * d2 + 100 * d3 + 1000 * d4;}static void ReallocVector(SEXP s, int length){long size;switch (TYPEOF(s)) {case CHARSXP:size = 1 + BYTE2VEC(length + 1);break;case LGLSXP:case INTSXP:if (length <= 0) size = 0;else size = 1 + INT2VEC(length);break;case REALSXP:if (length <= 0) size = 0;else size = 1 + FLOAT2VEC(length);break;case CPLXSXP:if (length <= 0) size = 0;else size = 1 + COMPLEX2VEC(length);break;case STRSXP:case VECSXP:case EXPRSXP:if (length <= 0) size = 0;else size = 1 + PTR2VEC(length);break;default:error("invalid type in ReallocVector"); size=0;}if (R_VMax - R_VTop < size)error("restore memory exhausted (should not happen)");LENGTH(s) = length;if (size > 0) {CHAR(s) = (char *) (R_VTop + 1);BACKPOINTER(*R_VTop) = s;R_VTop += size;}else CHAR(s) = (char*)0;}static void ReallocString(SEXP s, int length){long size = 1 + BYTE2VEC(length + 1);if (R_VMax - R_VTop < size)error("restore memory exhausted (should not happen)");if (TYPEOF(s) != CHARSXP)error("ReallocString: type conflict");CHAR(s) = (char*)(R_VTop + 1);LENGTH(s) = length;TAG(s) = R_NilValue;NAMED(s) = 0;ATTRIB(s) = R_NilValue;BACKPOINTER(*R_VTop) = s;R_VTop += size;}static void MarkSave(SEXP s){int i, len;if (s == R_NilValue|| s == R_GlobalEnv|| s == R_UnboundValue|| s == R_MissingArg) return;if (s && !MARK(s)) {MARK(s) = 1;if (ATTRIB(s) != R_NilValue)MarkSave(ATTRIB(s));switch (TYPEOF(s)) {case BUILTINSXP:case SPECIALSXP:NSave++;break;case SYMSXP:NSymbol++;break;case CHARSXP:NSave++;NVSize += 1 + BYTE2VEC(LENGTH(s) + 1);break;case LGLSXP:case INTSXP:NSave++;NVSize += 1 + INT2VEC(LENGTH(s));break;case REALSXP:NSave++;NVSize += 1 + FLOAT2VEC(LENGTH(s));break;case CPLXSXP:NSave++;NVSize += 1 + COMPLEX2VEC(LENGTH(s));break;case STRSXP:case VECSXP:case EXPRSXP:NSave++;NVSize += 1 + PTR2VEC(len=LENGTH(s));for (i=0; i < len; i++)MarkSave(VECTOR(s)[i]);break;case ENVSXP:NSave++;MarkSave(FRAME(s));MarkSave(ENCLOS(s));break;case CLOSXP:case PROMSXP:case LISTSXP:case LANGSXP:case DOTSXP:NSave++;MarkSave(TAG(s));MarkSave(CAR(s));MarkSave(CDR(s));break;}}}static int NodeToOffset(SEXP s){if (s == R_NilValue) return -1;if (s == R_GlobalEnv) return -2;if (s == R_UnboundValue) return -3;if (s == R_MissingArg) return -4;return s - R_NHeap;;}static SEXP OffsetToNode(int offset){int l, m, r;if (offset == -1) return R_NilValue;if (offset == -2) return R_GlobalEnv;if (offset == -3) return R_UnboundValue;if (offset == -4) return R_MissingArg;/* binary search for offset */l = 0;r = NTotal - 1;do {m = (l + r) / 2;if (offset < OldOffset[m])r = m - 1;elsel = m + 1;}while (offset != OldOffset[m] && l <= r);if (offset == OldOffset[m]) return NewAddress[m];error("unresolved node during restore");return R_NilValue;/* for -Wall */}static void DataSave(SEXP s, FILE *fp){int i, j, k, l, n;/* compute the storage requirements *//* and write these to the save file *//* NSymbol = # of symbols written *//* NSave = # of symbols written *//* NVSize = # of vector cells written */NSave = 0;NSymbol = 0;NVSize = 0;unmarkPhase();MarkSave(s);OutInit(fp);OutInteger(fp, NSymbol); OutSpace(fp);OutInteger(fp, NSave); OutSpace(fp);OutInteger(fp, NVSize); OutNewline(fp);/* write out any required symbols */k = 0; n = 0;for (i = 0; i < R_NSize; i++) {if (MARK(&R_NHeap[i])) {if (TYPEOF(&R_NHeap[i]) == SYMSXP) {OutInteger(fp, n);OutSpace(fp);OutInteger(fp, NodeToOffset(&R_NHeap[i]));OutSpace(fp);OutString(fp, CHAR(PRINTNAME(&R_NHeap[i])));OutNewline(fp);k++;}n++;}}if (k != NSymbol || n != NSymbol+NSave)error("symbol count conflict");/* write out the forwarding address table */k = 0; n = 0;for (i = 0; i < R_NSize; i++) {if (MARK(&R_NHeap[i])) {if (TYPEOF(&R_NHeap[i]) != SYMSXP) {OutInteger(fp, n);OutSpace(fp);OutInteger(fp, NodeToOffset(&R_NHeap[i]));OutNewline(fp);k++;}n++;}}if (k != NSave || n != NSymbol+NSave)error("node count conflict");k = 0; n = 0;for (i = 0; i < R_NSize; i++) {if (MARK(&R_NHeap[i])) {if (TYPEOF(&R_NHeap[i]) != SYMSXP) {OutInteger(fp, n);OutSpace(fp);OutInteger(fp, TYPEOF(&R_NHeap[i]));OutSpace(fp);OutInteger(fp, OBJECT(&R_NHeap[i]));OutSpace(fp);OutInteger(fp, LEVELS(&R_NHeap[i]));OutSpace(fp);OutInteger(fp, NodeToOffset(ATTRIB(&R_NHeap[i])));OutSpace(fp);switch (TYPEOF(&R_NHeap[i])) {case LISTSXP:case LANGSXP:case CLOSXP:case PROMSXP:case ENVSXP:OutInteger(fp, NodeToOffset(CAR(&R_NHeap[i])));OutSpace(fp);OutInteger(fp, NodeToOffset(CDR(&R_NHeap[i])));OutSpace(fp);OutInteger(fp, NodeToOffset(TAG(&R_NHeap[i])));OutNewline(fp);break;case SPECIALSXP:case BUILTINSXP:OutInteger(fp, strlen(PRIMNAME(&R_NHeap[i])));OutSpace(fp);OutString(fp, PRIMNAME(&R_NHeap[i]));OutNewline(fp);break;case CHARSXP:OutInteger(fp, LENGTH(&R_NHeap[i]));OutSpace(fp);OutString(fp, CHAR(&R_NHeap[i]));OutNewline(fp);break;case REALSXP:l = LENGTH(&R_NHeap[i]);OutInteger(fp, l);OutNewline(fp);for (j = 0; j < l; j++) {OutReal(fp, REAL(&R_NHeap[i])[j]);if ((j + 1) % 10 == 0 || j == l - 1)OutNewline(fp);elseOutSpace(fp);}break;case CPLXSXP:l = LENGTH(&R_NHeap[i]);OutInteger(fp, l);OutNewline(fp);for (j = 0; j < l; j++) {OutComplex(fp, COMPLEX(&R_NHeap[i])[j]);if ((j + 1) % 10 == 0 || j == l - 1)OutNewline(fp);elseOutSpace(fp);}break;case INTSXP:case LGLSXP:l = LENGTH(&R_NHeap[i]);OutInteger(fp, l);OutNewline(fp);for (j = 0; j < l; j++) {OutInteger(fp, INTEGER(&R_NHeap[i])[j]);if ((j + 1) % 10 == 0 || j == l - 1)OutNewline(fp);elseOutSpace(fp);}break;case STRSXP:case VECSXP:case EXPRSXP:l = LENGTH(&R_NHeap[i]);OutInteger(fp, l);OutNewline(fp);for (j = 0; j < l; j++) {OutInteger(fp, NodeToOffset(VECTOR(&R_NHeap[i])[j]));if ((j + 1) % 10 == 0 || j == l - 1)OutNewline(fp);elseOutSpace(fp);}}k++;}n++;}}if (k != NSave) error("node count conflict");/* write out the offset of the list */OutInteger(fp, NodeToOffset(s));OutNewline(fp);OutTerm(fp);}static void RestoreSEXP(SEXP s, FILE *fp){unsigned int j;int len;TYPEOF(s) = InInteger(fp);if (VersionId) {switch(VersionId) {case 16:/* In the version 0.16.1 -> 0.50 switch *//* we really introduced complex values *//* and found that numeric/complex numbers *//* had to be contiguous. Hence this switch */if (TYPEOF(s) == STRSXP)TYPEOF(s) = CPLXSXP;else if (TYPEOF(s) == CPLXSXP)TYPEOF(s) = STRSXP;break;default:error("restore compatibility error - no version %d compatibility", VersionId);}}/* Map old factors to new ... (0.61->0.62) */if (TYPEOF(s) == 11 || TYPEOF(s) == 12)TYPEOF(s) = 13;OBJECT(s) = InInteger(fp);LEVELS(s) = InInteger(fp);ATTRIB(s) = OffsetToNode(InInteger(fp));switch (TYPEOF(s)) {case LISTSXP:case LANGSXP:case CLOSXP:case PROMSXP:case ENVSXP:CAR(s) = OffsetToNode(InInteger(fp));CDR(s) = OffsetToNode(InInteger(fp));TAG(s) = OffsetToNode(InInteger(fp));break;case SPECIALSXP:case BUILTINSXP:len = InInteger(fp);AllocBuffer(MAXELTSIZE - 1);PRIMOFFSET(s) = StrToInternal(InString(fp));break;case CHARSXP:LENGTH(s) = len = InInteger(fp);ReallocString(s, len);AllocBuffer(len);strcpy(CHAR(s), InString(fp));break;case REALSXP:LENGTH(s) = len = InInteger(fp);ReallocVector(s, len);for (j = 0; j < len; j++)REAL(s)[j] = InReal(fp);break;case CPLXSXP:LENGTH(s) = len = InInteger(fp);ReallocVector(s, len);for (j = 0; j < len; j++)COMPLEX(s)[j] = InComplex(fp);break;case INTSXP:case LGLSXP:LENGTH(s) = len = InInteger(fp);;ReallocVector(s, len);for (j = 0; j < len; j++)INTEGER(s)[j] = InInteger(fp);break;case STRSXP:case VECSXP:case EXPRSXP:LENGTH(s) = len = InInteger(fp);ReallocVector(s, len);for (j = 0; j < len; j++) {VECTOR(s)[j] = OffsetToNode(InInteger(fp));}break;}}static SEXP DataLoad(FILE *fp){int i, j, vsmall, nsmall;char *vmaxsave, msg[512], s[256];;/* read in the size information */InInit(fp);NSymbol = InInteger(fp);NSave = InInteger(fp);NVSize = InInteger(fp);NTotal = NSymbol + NSave;/* allocate the forwarding-address tables *//* these are non-relocatable, so we must *//* save the current non-relocatable base */vmaxsave = vmaxget();OldOffset = (int*)R_alloc(NSymbol+NSave, sizeof(int));NewAddress = (SEXP*)R_alloc(NSymbol+NSave, sizeof(SEXP));for (i = 0 ; i < NTotal ; i++) {OldOffset[i] = 0;NewAddress[i] = R_NilValue;}/* read in the required symbols *//* expanding the symbol table and *//* computing the forwarding addresses */for (i = 0 ; i < NSymbol ; i++) {j = InInteger(fp);OldOffset[j] = InInteger(fp);AllocBuffer(MAXELTSIZE - 1);NewAddress[j] = install(InString(fp));}/* symbols are all installed *//* gc() and check space */gc();/* a gc after this point will be a disaster *//* because nothing will have been protected */if(DLstartup) {vsmall = NVSize - ((VECREC *)vmaxget() - R_VTop) ;nsmall = NSave - R_Collected;msg[0] = '\0';if(vsmall > 0) {sprintf(s, "vector heap is too small to restore data -- need about %dM\n", (int)ceil((16.*(R_VSize + vsmall))/Mega));strcat(msg, s);}if (nsmall > 0) {sprintf(s, "cons heap is too small to restore data -- need about %dk\n", (int)ceil((R_NSize + nsmall)/1000.));strcat(msg, s);}if(vsmall > 0 || nsmall > 0)R_Suicide(msg);} else {if ((VECREC *)vmaxget() - R_VTop < NVSize)error("vector heap is too small to restore data");if (R_Collected < NSave)error("cons heap is too small to restore data");}/* build the full forwarding table *//* allocating SEXPs from the free list */for (i = 0 ; i < NSave ; i++) {j = InInteger(fp);OldOffset[j] = InInteger(fp);NewAddress[j] = R_FreeSEXP;R_FreeSEXP = CDR(R_FreeSEXP);}/* restore the saved nodes */for (i = 0 ; i < NSave ; i++) {RestoreSEXP(NewAddress[InInteger(fp)], fp);}/* restore the heap */vmaxset(vmaxsave);/* clean the string buffer */AllocBuffer(-1);/* return the "top-level" object *//* this is usually a list */i = InInteger(fp);InTerm(fp);return OffsetToNode(i);}void R_SaveToFile(SEXP obj, FILE *fp, int ascii){if (ascii) {R_WriteMagic(fp, R_MAGIC_ASCII);AsciiSave(obj, fp);}else {#ifdef HAVE_RPC_XDR_HR_WriteMagic(fp, R_MAGIC_XDR);XdrSave(obj, fp);#elseR_WriteMagic(fp, R_MAGIC_BINARY);BinarySave(obj, fp);#endif}}SEXP R_LoadFromFile(FILE *fp, int startup){DLstartup = startup; /* different handling of errors */switch(R_ReadMagic(fp)) {#ifdef HAVE_RPC_XDR_Hcase R_MAGIC_XDR:return(XdrLoad(fp));#endifcase R_MAGIC_BINARY:return(BinaryLoad(fp));case R_MAGIC_ASCII:return(AsciiLoad(fp));case R_MAGIC_BINARY_VERSION16:return(BinaryLoadOld(fp, 16));case R_MAGIC_ASCII_VERSION16:return(AsciiLoadOld(fp, 16));default:fclose(fp);error("restore file corrupted -- no data loaded");return(R_NilValue);/* for -Wall */}}/* Interpreter Interface Functions */SEXP do_save(SEXP call, SEXP op, SEXP args, SEXP env){SEXP s, t;int len, j;FILE *fp;checkArity(op, args);if (TYPEOF(CAR(args)) != STRSXP)errorcall(call, "first argument must be a character vector");if (TYPEOF(CADR(args)) != STRSXP)errorcall(call, "second argument must be a string");if (TYPEOF(CADDR(args)) != LGLSXP)errorcall(call, "third argument must be a logical vector");fp = R_fopen(R_ExpandFileName(CHAR(STRING(CADR(args))[0])), "wb");if (!fp)errorcall(call, "unable to open file");len = length(CAR(args));PROTECT(s = allocList(len));t = s;for (j = 0; j < len; j++, t = CDR(t)) {TAG(t) = install(CHAR(STRING(CAR(args))[j]));CAR(t) = findVar(TAG(t), R_GlobalContext->sysparent);if (CAR(t) == R_UnboundValue)error("Object \"%s\" not found", CHAR(PRINTNAME(TAG(t))));}R_SaveToFile(s, fp, INTEGER(CADDR(args))[0]);UNPROTECT(1);fclose(fp);return R_NilValue;}/* These functions convert old (pairlist) lists into new *//* (vectorlist) lists. The conversion can be defeated by *//* hiding things inside closures, but it is doubtful that *//* anyone has done this. */static SEXP ConvertPairToVector(SEXP);static SEXP ConvertAttributes(SEXP attrs){SEXP ap = attrs;while (ap != R_NilValue) {if (TYPEOF(CAR(ap)) == LISTSXP)CAR(ap) = ConvertPairToVector(CAR(ap));ap = CDR(ap);}return attrs;}#ifdef NOTYET/* It may be that there are LISTXP hiding in closures. *//* This will convert them. */static SEXP ConvertEnvironment(SEXP env){SEXP frame = FRAME(env);while (frame != R_NilValue) {if (TYPEOF(CAR(frame)) == LISTSXP)CAR(frame) = ConvertPairToVector(CAR(frame));frame = CDR(frame);}return env;}#endifstatic SEXP ConvertPairToVector(SEXP obj){int i, n;switch (TYPEOF(obj)) {case LISTSXP:PROTECT(obj = PairToVectorList(obj));n = length(obj);for (i = 0; i < n; i++)VECTOR(obj)[i] = ConvertPairToVector(VECTOR(obj)[i]);UNPROTECT(1);break;case VECSXP:break;default:;}ATTRIB(obj) = ConvertAttributes(ATTRIB(obj));return obj;}void R_LoadSavedData(FILE *fp, SEXP aenv){SEXP a, ans;ans = R_LoadFromFile(fp, 0);/* Store the components of the list in the Global Env *//* We either replace the existing objects in the Global *//* Environment or establish new bindings for them. *//* Note that we try to convert old "pairlist" objects *//* to new "pairlist" objects. */PROTECT(a = ans);while (a != R_NilValue) {#ifdef OLDfor (e = FRAME(aenv); e != R_NilValue ; e = CDR(e)) {if (TAG(e) == TAG(a)) {CAR(e) = CAR(a);a = CDR(a);CAR(a) = ConvertPairToVector(CAR(a)); /* PAIRLIST conv */goto NextItem;}}e = a;a = CDR(a);UNPROTECT(1);PROTECT(a);CDR(e) = FRAME(aenv);FRAME(aenv) = e;CAR(e) = ConvertPairToVector(CAR(e)); /* PAIRLIST conv */NextItem:;#elsedefineVar(TAG(a), ConvertPairToVector(CAR(a)), aenv);a = CDR(a);#endif}UNPROTECT(1);}SEXP do_load(SEXP call, SEXP op, SEXP args, SEXP env){SEXP fname, aenv;FILE *fp;checkArity(op, args);if (!isValidString(fname = CAR(args)))errorcall (call, "first argument must be a file name\n");/* GRW 1/26/99 GRW : added environment parameter so that *//* the loaded objects can be placed where desired */aenv = CADR(args);if (TYPEOF(aenv) != ENVSXP && aenv != R_NilValue)error("invalid envir argument");/* Process the saved file to obtain a list of saved objects. */fp = R_fopen(R_ExpandFileName(CHAR(STRING(fname)[0])), "rb");if (!fp)errorcall(call, "unable to open file");R_LoadSavedData(fp, aenv);fclose(fp);return R_NilValue;}#ifdef HAVE_HDF5#define STRING2REF_CONV "string->ref"#define REF2STRING_CONV "ref->string"#define ROWNAMES "row.names"static herr_tref_string (hid_t sid, hid_t did, H5T_cdata_t *cdata,size_t count, size_t stride,void *buf, void *bkg,hid_t dset_xfer_plid){if (cdata->command == H5T_CONV_CONV){SEXPREC *srcbuf[count];char *destbuf = buf;SEXPREC **recptr = srcbuf;size_t i;size_t maxlen = H5Tget_size (did);memcpy (srcbuf, buf, sizeof (srcbuf));for (i = 0; i < count; i++){strncpy (destbuf, CHAR (*recptr), maxlen);recptr++;destbuf += maxlen;}}return 0;}static herr_tstring_ref (hid_t sid, hid_t did, H5T_cdata_t *cdata,size_t count, size_t stride,void *buf, void *bkg,hid_t dset_xfer_plid){if (cdata->command == H5T_CONV_CONV){size_t size = H5Tget_size (sid);unsigned char srcbuf[size * count], *srcptr = srcbuf;size_t i;memcpy (srcbuf, buf, sizeof (srcbuf));for (i = 0; i < count;i ++){((SEXPREC **)buf)[i] = mkChar (srcptr);srcptr += size;}}return 0;}struct permute_info {SEXP call;int writeflag;SEXPTYPE type;unsigned rank;hssize_t *dims;hssize_t *coord;hid_t dataset;hid_t memtid;hid_t space;hid_t mspace;void *buf;};static voidpermute (struct permute_info *pinfo, unsigned dimnum){hssize_t i;if (dimnum < pinfo->rank){for (i = 0; i < pinfo->dims[dimnum]; i++){pinfo->coord[dimnum] = i;permute (pinfo, dimnum + 1);}}else{unsigned offset, mult;if (H5Sselect_elements (pinfo->space, H5S_SELECT_SET, 1,(const hssize_t **) pinfo->coord) < 0)errorcall (pinfo->call, "Unable to select file elements");offset = pinfo->coord[0];mult = 1;for (i = 1; i < pinfo->rank; i++){mult *= pinfo->dims[i - 1];offset += pinfo->coord[i] * mult;}{void *pointaddr;switch (pinfo->type){case STRSXP: case VECSXP:pointaddr = &((SEXPREC **)pinfo->buf)[offset];break;case REALSXP:pointaddr = &((double *)pinfo->buf)[offset];break;case INTSXP: case LGLSXP:pointaddr = &((int *)pinfo->buf)[offset];break;default:errorcall (pinfo->call, "No support for R type: %d", pinfo->type);pointaddr = &offset;/* unreached; -Wall */}if (pinfo->writeflag){if (H5Dwrite (pinfo->dataset,pinfo->memtid,pinfo->mspace,pinfo->space,H5P_DEFAULT,pointaddr) < 0)errorcall (pinfo->call, "Unable to write dataset");}else{if (H5Dread (pinfo->dataset,pinfo->memtid,pinfo->mspace,pinfo->space,H5P_DEFAULT,pointaddr) < 0)errorcall (pinfo->call, "Unable to read dataset");}}}}static hid_tmake_sexp_ref_type (SEXP call){hid_t memtid;if ((memtid = H5Tcopy (H5T_STD_REF_OBJ)) < 0)errorcall (call, "Unable to copy H5T_STD_REF_OBJ");if (H5Tset_size (memtid, sizeof (SEXPREC *)) < 0)errorcall (call, "unable to set size of reference type");return memtid;}static hid_tget_string_type (SEXP call, SEXP vec){hid_t stid;unsigned vecpos;size_t maxstrlen = 0;for (vecpos = 0; vecpos < LENGTH (vec); vecpos++){SEXP stritem = STRING (vec)[vecpos];if (LENGTH (stritem) > maxstrlen)maxstrlen = LENGTH (stritem);}if ((stid = H5Tcopy (H5T_C_S1)) < 0)errorcall (call, "Cannot copy string type");if (H5Tset_size (stid, maxstrlen + 1) < 0)errorcall (call, "Cannot set size of string type");return stid;}static voidvector_io (SEXP call, int writeflag, hid_t dataset, hid_t space, SEXP obj){int rank = H5Sget_simple_extent_ndims (space);hsize_t mdims[1] = {1};hsize_t dims[rank], maxdims[rank];SEXPTYPE type = TYPEOF (obj);hid_t memtid, tid, mspace;void *buf;if ((tid = H5Dget_type (dataset)) < 0)errorcall (call, "Unable to get type for dataset");switch (type) {case STRSXP:memtid = make_sexp_ref_type (call);buf = STRING (obj);break;case REALSXP:memtid = H5T_NATIVE_DOUBLE;buf = REAL (obj);break;case INTSXP:memtid = H5T_NATIVE_INT;buf = INTEGER (obj);break;case LGLSXP:memtid = H5T_NATIVE_UINT;buf = INTEGER (obj);break;default:errorcall (call, "Can't get type for R type: %d (IO)", type);/* unreached (-Wall): */ memtid = tid; buf = &tid;}if (H5Sget_simple_extent_dims (space, dims, maxdims) < 0)errorcall (call, "Unable to get dimensions of space");if ((mspace = H5Screate_simple (1, mdims, NULL)) < 0)errorcall (call, "Unable to create point space");{struct permute_info pinfo;hssize_t coord[rank];pinfo.call = call;pinfo.writeflag = writeflag;pinfo.type = type;pinfo.rank = rank;pinfo.coord = coord;pinfo.dims = dims;pinfo.dataset = dataset;pinfo.memtid = memtid;pinfo.space = space;pinfo.mspace = mspace;pinfo.buf = buf;permute (&pinfo, 0);}if (H5Sclose (mspace) < 0)errorcall (call, "Unable to close point space");if (type == STRSXP) {if (H5Tclose (memtid) < 0)errorcall (call, "Unable to close string reference type");}}static voidhdf5_save_attributes (SEXP call, hid_t loc_id, SEXP val){SEXP l;for (l = ATTRIB (val); l != R_NilValue; l = CDR (l)){SEXP attr = CAR (l);SEXPTYPE type = TYPEOF (attr);const char *name = CHAR (PRINTNAME (TAG (l)));void *buf;hid_t tid, memtid;hid_t sid, aid;unsigned count = LENGTH (attr);/*SEXPREC *stringptrs[count];*/if (TAG (l) == R_RowNamesSymbol|| TAG (l) == R_ClassSymbol|| TAG (l) == R_NamesSymbol|| TAG (l) == R_DimNamesSymbol)continue;{hsize_t dims[1];dims[0] = count;if ((sid = H5Screate_simple (1, dims, NULL)) < 0)errorcall (call,"unable to create vector space for attribute `%s'", name);}if (type == STRSXP){memtid = make_sexp_ref_type (call);tid = get_string_type (call, attr);buf = STRING (attr);}else if (type == LGLSXP){memtid = H5T_NATIVE_INT;tid = H5Tcopy (H5T_NATIVE_UINT);H5Tset_precision (tid, 1);H5Tset_size (tid, 1);buf = INTEGER (attr);}else if (type == INTSXP){memtid = H5T_NATIVE_INT;tid = H5T_NATIVE_INT;buf = INTEGER (attr);}else if (type == REALSXP){memtid = H5T_NATIVE_DOUBLE;tid = H5T_NATIVE_DOUBLE;buf = REAL (attr);}elseabort ();if ((aid = H5Acreate (loc_id, name, tid, sid, H5P_DEFAULT)) < 0)errorcall (call, "unable to create attribute `%s'", name);if (H5Awrite (aid, memtid, buf) < 0)errorcall (call, "unable to write attribute `%s'", name);if (H5Aclose (aid) < 0)errorcall (call, "unable to close attribute `%s'", name);if (type == STRSXP){if (H5Tclose (memtid) < 0)errorcall (call,"unable to close string reference type `%s'",name);}if (type == LGLSXP || type == STRSXP){if (H5Tclose (tid) < 0)errorcall (call, "unable to close output type `%s'", name);}if (H5Sclose (sid) < 0)errorcall (call, "unable to close space for attribute `%s'", name);}}static voidhdf5_write_vector (SEXP call, hid_t id, const char *symname, SEXP val){unsigned i, rank;SEXP dimvec;hid_t space, dataset;SEXPTYPE type = TYPEOF (val);hid_t tid;dimvec = getAttrib (val, R_DimSymbol);rank = (dimvec == R_NilValue) ? 1 : LENGTH (dimvec);{hsize_t dims[rank];if (rank > 1)for (i = 0; i < rank; i++)dims[i] = INTEGER (dimvec)[i];elsedims[0] = length(val);if ((space = H5Screate_simple (rank, dims, NULL)) < 0)errorcall (call, "Unable to create file dataspace");if (type == STRSXP)tid = get_string_type (call, val);else if (type == LGLSXP){tid = H5Tcopy (H5T_NATIVE_UINT);H5Tset_precision (tid, 1);H5Tset_size (tid, 1);}else if (type == INTSXP)tid = H5T_NATIVE_INT;else if (type == REALSXP)tid = H5T_NATIVE_DOUBLE;else {errorcall (call, "Can't get type for R type: %d (Creating)", type);tid = H5T_NATIVE_INT;/*unreached; -Wall*/}if ((dataset = H5Dcreate (id,symname,tid,space,H5P_DEFAULT)) < 0)errorcall (call, "Unable to create dataset");vector_io (call, TRUE, dataset, space, val);hdf5_save_attributes (call, dataset, val);if (type == LGLSXP || type == STRSXP)if (H5Tclose (tid) < 0)errorcall (call, "Unable to close type");if (H5Dclose (dataset) < 0)errorcall (call, "Unable to close dataset");if (H5Sclose (space) < 0)errorcall (call, "Unable to close space");}}static voidhdf5_write_string (SEXP call, hid_t fid, const char *symname, const char *str){hid_t stringtype;hid_t dataset;hid_t dataspace;dataspace = H5Screate (H5S_SCALAR);stringtype = H5Tcopy (H5T_C_S1);H5Tset_size (stringtype, strlen (str) + 1);if ((dataset = H5Dcreate (fid,symname,stringtype,dataspace,H5P_DEFAULT)) < 0)errorcall (call, "Unable to create dataset");if (H5Dwrite (dataset,stringtype,H5S_ALL,H5S_ALL,H5P_DEFAULT,str) < 0)errorcall (call, "Unable to write dataset");H5Dclose (dataset);H5Sclose (dataspace);H5Tclose (stringtype);}static unsignedalign (unsigned offset, unsigned alignto){#if 0unsigned mask = alignto - 1;if ((offset & mask) == 0)return offset;elsereturn (offset + alignto) & ~mask;#elsereturn offset;#endif}static voidcreate_rownames_dataset_attribute (SEXP call, hid_t dataset, SEXP rownames){hid_t stringtid = get_string_type (call, rownames);hid_t rtid = make_sexp_ref_type (call);hid_t rnattrib, rndataspace;unsigned rowcount = LENGTH (rownames);hsize_t dims[1];dims[0] = rowcount;if ((rndataspace = H5Screate_simple (1, dims, NULL)) < 0)errorcall (call, "Unable to create row names vector space");if ((rnattrib = H5Acreate (dataset, ROWNAMES,stringtid, rndataspace, H5P_DEFAULT)) < 0)errorcall (call, "unable to create row names dataset");if (H5Awrite (rnattrib, rtid, STRING (rownames)) < 0)errorcall (call, "unable to write row names dataset");if (H5Aclose (rnattrib) < 0)errorcall (call, "unable to close row names dataset");if (H5Sclose (rndataspace) < 0)errorcall (call, "unable to close row names dataspace");if (H5Tclose (stringtid) < 0)errorcall (call, "unable to close row names string type");if (H5Tclose (rtid) < 0)errorcall (call, "unable to close reference type");}static voidhdf5_save_object (SEXP call, hid_t fid, const char *symname, SEXP val){if (isFrame (val)){unsigned colcount = length (val), pos;size_t offsets[colcount];hid_t hdftypes[colcount];hid_t hdfbooltype;size_t size = 0;if ((hdfbooltype = H5Tcopy (H5T_NATIVE_UINT)) < 0)errorcall (call, "Cannot copy unsigned integer type");if (H5Tset_precision (hdfbooltype, 1) < 0)errorcall (call, "Cannot set precision of boolean type");if (H5Tset_size (hdfbooltype, 1) < 0)errorcall (call, "Cannot set size of boolean type");for (pos = 0; pos < colcount; pos++){SEXPTYPE type = TYPEOF (VECTOR (val)[pos]);switch (type){case REALSXP:hdftypes[pos] = H5T_NATIVE_DOUBLE;break;case INTSXP:hdftypes[pos] = H5T_NATIVE_INT;break;case STRSXP:hdftypes[pos] = get_string_type (call, VECTOR (val)[pos]);break;case LGLSXP:hdftypes[pos] = hdfbooltype;break;default:errorcall (call,"No support for converting R type: %d to HDF5",type);break;}if (H5Tget_class (hdftypes[pos]) == H5T_STRING)size = align (size, 8);elsesize = align (size, H5Tget_size (hdftypes[pos]));offsets[pos] = size;size += H5Tget_size (hdftypes[pos]);}size = align (size, H5Tget_size (hdftypes[0]));{hid_t ctid;hid_t dataset;hid_t dataspace;unsigned rowcount = length (VECTOR (val)[0]);{hsize_t dims[1];dims[0] = rowcount;if ((dataspace = H5Screate_simple (1, dims, NULL)) < 0)errorcall (call, "Unable to create dataframe vector space");}{SEXP colnames = getAttrib (val, R_NamesSymbol);if ((ctid = H5Tcreate (H5T_COMPOUND, size)) < 0)errorcall (call, "unable to create compound type");for (pos = 0; pos < colcount; pos++)if (H5Tinsert (ctid,CHAR (STRING (colnames)[pos]),offsets[pos],hdftypes[pos]) < 0)errorcall (call, "unable to insert type into compound type");}if (H5Tpack (ctid) < 0)errorcall (call, "Unable to pack type");if (H5Tlock (ctid) < 0)errorcall (call, "Unable to lock type");if ((dataset = H5Dcreate (fid, symname,ctid, dataspace, H5P_DEFAULT)) < 0)errorcall (call, "unable to create dataframe dataset");{unsigned ri;unsigned char buf[rowcount][size];for (ri = 0; ri < rowcount; ri++)for (pos = 0; pos < colcount; pos++){SEXP item = VECTOR (val)[pos];SEXPTYPE type = TYPEOF (item);void *ptr = &buf[ri][offsets[pos]];switch (type){case REALSXP:memcpy (ptr, &REAL (item)[ri], sizeof (double));break;case INTSXP:memcpy (ptr, &INTEGER (item)[ri], sizeof (int));break;case STRSXP:{SEXP stritem = STRING (item)[ri];/*size_t len = LENGTH (stritem);*/memset (ptr, 0, H5Tget_size (hdftypes[pos]));strcpy ((char *)ptr, CHAR (stritem));}break;case LGLSXP:*(unsigned char *)ptr = INTEGER (item)[ri];break;default:abort ();}}if (H5Dwrite (dataset,ctid,dataspace,dataspace,H5P_DEFAULT,buf) < 0)errorcall (call, "Unable to write dataframe");}hdf5_save_attributes (call, dataset, val);{SEXP rownames = getAttrib (val, R_RowNamesSymbol);if (rownames != R_NilValue)create_rownames_dataset_attribute (call, dataset, rownames);}if (H5Dclose (dataset) < 0)errorcall (call, "Cannot close dataset");if (H5Sclose (dataspace) < 0)errorcall (call, "Cannot close dataspace");}for (pos = 0; pos < colcount; pos++)if (H5Tget_class (hdftypes[pos]) == H5T_STRING)if (H5Tclose (hdftypes[pos]) < 0)errorcall (call, "Cannot close string type");if (H5Tclose (hdfbooltype) < 0)errorcall (call, "Cannot close boolean type");}else if (isNull (val)){}else{SEXPTYPE type = TYPEOF (val);switch (type){case LGLSXP: case INTSXP: case REALSXP: case STRSXP:hdf5_write_vector (call, fid, symname, val);break;case LISTSXP: case VECSXP:{unsigned len = length (val);hid_t gid;unsigned pos;char buf[(sizeof (pos) * 8 / 3 + 1) + 1];if ((gid = H5Gcreate (fid, symname, len * 8)) < 0)errorcall (call, "unable to create group");if (type == LISTSXP){SEXP l;for (l = val, pos = 0; l != R_NilValue; l = CDR (l), pos++){SEXP s = CAR (l);if (!isNull (TAG (l)))hdf5_save_object (call, gid,CHAR (PRINTNAME (TAG (l))), s);else{sprintf (buf, "%u", pos);hdf5_save_object (call, gid, buf, s);}}}else{for (pos = 0; pos < len; pos++){SEXP s = VECTOR (val)[pos];SEXP names = getAttrib (val, R_NamesSymbol);if (!isNull (names))hdf5_save_object (call, gid,CHAR (STRING (names) [pos]),s);else{sprintf (buf, "%u", pos);hdf5_save_object (call, gid, buf, s);}}}hdf5_save_attributes (call, gid, val);if (H5Gclose (gid) < 0)errorcall (call, "unable to close group");}break;case SYMSXP:{const char *pn = CHAR (PRINTNAME (val));hdf5_write_string (call, fid, symname, pn);}break;default:errorcall (call, "unhandled type: %d", type);break;}}}static voidhdf5_save_symbol (SEXP call, hid_t fid, SEXP sym, SEXP env){SEXP val;val = findVar (sym, env);hdf5_save_object (call, fid, CHAR (PRINTNAME (sym)), val);}SEXP do_hdf5save (SEXP call, SEXP op, SEXP args, SEXP env){const char *path;hid_t fid;SEXP s;checkArity (op, args);if (length (args) < 2)errorcall (call, "Two arguments are required: HDF-path and an object");if (TYPEOF (CAR (args)) != STRSXP)errorcall (call, "first argument must be a pathname");path = CHAR (STRING (CAR (args))[0]);H5dont_atexit ();if (H5Tregister (H5T_PERS_SOFT,REF2STRING_CONV,H5T_STD_REF_OBJ,H5T_C_S1, ref_string) < 0)errorcall (call, "Unable to register ref->string converter");if ((fid = H5Fcreate (path, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0)errorcall (call, "unable to create HDF file: %s", path);for (s = CDR (args); s != R_NilValue; s = CDR (s))hdf5_save_symbol (call, fid, CAR (s), env);if (H5Fclose (fid) < 0)errorcall (call, "unable to close HDF file: %s", path);if (H5Tunregister (H5T_PERS_SOFT, REF2STRING_CONV, -1, -1, ref_string) < 0)errorcall (call, "Unable to unregister ref->string converter");return R_NilValue;}struct hdf5_iterate_info {SEXP call;void (*add) (struct hdf5_iterate_info *, const char *, SEXP);SEXP env;SEXP ret;};static voidadd_to_list (struct hdf5_iterate_info *iinfo, const char *name, SEXP obj){PROTECT (iinfo->ret);iinfo->ret = CONS (obj, iinfo->ret);TAG (iinfo->ret) = install ((char *) name);UNPROTECT (1);}static SEXPcollect (SEXP call, hid_t id, H5G_iterate_t iterate_func, SEXP env){struct hdf5_iterate_info iinfo;iinfo.call = call;iinfo.add = add_to_list;iinfo.ret = R_NilValue;iinfo.env = env;if (H5Giterate (id, ".", NULL, iterate_func, &iinfo) < 0)errorcall (call, "unable to collect HDF group");{SEXP nl = R_NilValue, l;SEXP rl = iinfo.ret;PROTECT (rl);l = rl;while (l != R_NilValue){PROTECT (nl);nl = CONS (CAR (l), nl);TAG (nl) = TAG (l);UNPROTECT (1);l = CDR (l);}UNPROTECT (1);return nl;}}static voidload_rownames_dataset_attribute (SEXP call, hid_t dataset, SEXP vec){hid_t rnattrib, rnspace, rntid;unsigned rowcount;SEXP rownames;H5E_auto_t errfunc;void *client_data;H5Eset_auto (NULL, NULL);rnattrib = H5Aopen_name (dataset, ROWNAMES);H5Eget_auto (&errfunc, &client_data);H5Eset_auto (errfunc, client_data);if (rnattrib < 0)return;if ((rnspace = H5Aget_space (rnattrib)) < 0)errorcall (call, "could not get space for rownames attribute");if ((rntid = H5Aget_type (rnattrib)) < 0)errorcall (call, "could not get element type of rownames attribute");if (H5Sget_simple_extent_ndims (rnspace) != 1)errorcall (call, "rownames space should be of rank 1");{hsize_t dims[1], maxdims[1];if (H5Sget_simple_extent_dims (rnspace, dims, maxdims)< 0)errorcall (call, "can't get attribute space dims");rowcount = dims[0];}PROTECT (rownames = allocVector (STRSXP, rowcount));{SEXPREC *strptrs[rowcount];unsigned ri;hid_t rtid = make_sexp_ref_type (call);for (ri = 0; ri < rowcount; ri++)strptrs[ri] = STRING (rownames)[ri];if (H5Aread (rnattrib, rtid, strptrs) < 0)errorcall (call, "can't read rownames");for (ri = 0; ri < rowcount; ri++)STRING (rownames)[ri] = strptrs [ri];}setAttrib (vec, R_RowNamesSymbol, rownames);UNPROTECT (1);}struct hdf5_attribute_info {SEXP call;SEXP obj;const char *name;};static herr_thdf5_process_attribute (hid_t loc_id, const char *attrName, void *data){struct hdf5_attribute_info *ainfo = data;hid_t aid, sid, tid;H5T_class_t class;size_t tid_size;if ((aid = H5Aopen_name (loc_id, attrName)) < 0)errorcall (ainfo->call, "could not open attribute `%s'", attrName);if ((sid = H5Aget_space (aid)) < 0)errorcall (ainfo->call, "could not open space of attribute `%s'",attrName);if ((tid = H5Aget_type (aid)) < 0)errorcall (ainfo->call, "could not get type of attribute `%s'", attrName);if ((tid_size = H5Tget_size (tid)) < 0)errorcall (ainfo->call, "could not get size of attribute `%s' tid",attrName);if ((class = H5Tget_class (tid)) < 0)errorcall (ainfo->call, "could not get type class of attribute `%s'",attrName);{int rank;if ((rank = H5Sget_simple_extent_ndims (sid)) < 0)errorcall (ainfo->call,"could not get rank of attribute space `%s'",attrName);{hsize_t dims[rank];if (H5Sget_simple_extent_dims (sid, dims, NULL) < 0)errorcall (ainfo->call,"could not get extent of attribute space `%s'",attrName);if (rank == 1){unsigned count = dims[0];SEXPTYPE type;hid_t memtid;SEXP vec;void *buf;switch (class){case H5T_INTEGER:type = (tid_size == 1) ? LGLSXP : INTSXP;memtid = H5Tcopy (H5T_NATIVE_INT);break;case H5T_FLOAT:type = REALSXP;memtid = H5Tcopy (H5T_NATIVE_DOUBLE);break;case H5T_STRING:type = STRSXP;memtid = make_sexp_ref_type (ainfo->call);break;default:warningcall (ainfo->call, "skipping attribute `%s' due to type",attrName);goto done;}PROTECT (vec = allocVector (type, count));switch (class){case H5T_INTEGER:buf = INTEGER (vec);break;case H5T_FLOAT:buf = REAL (vec);break;case H5T_STRING:buf = STRING (vec);break;default:abort ();}if (H5Aread (aid, memtid, buf) < 0)errorcall (ainfo->call, "unable to read attribute `%s'", attrName);if (!isNull (ainfo->obj))setAttrib (ainfo->obj, install ((char *) attrName), vec);UNPROTECT (1);if (H5Tclose (memtid) < 0)errorcall (ainfo->call,"unable to close reference type in attribute `%s'",attrName);}elsewarningcall (ainfo->call, "skipping attribute `%s' due to rank",attrName);}}done:if (H5Sclose (sid) < 0)errorcall (ainfo->call, "unable to close attribute `%s' space", attrName);if (H5Tclose (tid) < 0)errorcall (ainfo->call, "unable to close attribute `%s' type", attrName);if (H5Aclose (aid) < 0)errorcall (ainfo->call, "unable to close attribute `%s'", attrName);return 0;}static voidhdf5_load_attributes (SEXP call, hid_t id, SEXP obj, const char *name){unsigned idx = 0;struct hdf5_attribute_info ainfo;ainfo.call = call;ainfo.obj = obj;ainfo.name = name;if (H5Aiterate (id, &idx, hdf5_process_attribute, &ainfo) < 0)errorcall (call, "unable to iterate over attributes");}static herr_thdf5_process_object (hid_t id, const char *name, void *client_data){struct hdf5_iterate_info *iinfo = client_data;H5G_stat_t statbuf;if (H5Gget_objinfo (id, name, 1, &statbuf) < 0)errorcall (iinfo->call, "Cannot query object `%s'", name);if (statbuf.type == H5G_GROUP){SEXP l;hid_t gid = H5Gopen (id, name);if (gid < 0)errorcall (iinfo->call, "unable to open group `%s'", name);PROTECT (l = collect (iinfo->call, gid, hdf5_process_object, iinfo->env));iinfo->add (iinfo, name, l);hdf5_load_attributes (iinfo->call, gid, l, name);UNPROTECT (1);if (H5Gclose (gid) < 0)errorcall (iinfo->call, "unable to close group");}else if (statbuf.type == H5G_DATASET){hid_t dataset, space, tid;int rank;SEXPTYPE type = NILSXP;/*H5T_class_t class;*/if ((dataset = H5Dopen (id, name)) < 0)errorcall (iinfo->call, "unable to load dataset `%s'", name);if ((tid = H5Dget_type (dataset)) < 0)errorcall (iinfo->call, "unable to get dataset type");switch (H5Tget_class (tid)){case H5T_INTEGER:if (H5Tget_precision (tid) == 1)type = LGLSXP;elsetype = INTSXP;break;case H5T_FLOAT:type = REALSXP;break;case H5T_STRING:type = STRSXP;break;case H5T_COMPOUND:type = VECSXP;break;default:errorcall (iinfo->call, "can't handle hdf type %d", tid);break;}if ((space = H5Dget_space (dataset)) < 0)errorcall (iinfo->call, "unable to get dataset space");if (H5Sis_simple (space) != TRUE)errorcall (iinfo->call, "space not simple");if ((rank = H5Sget_simple_extent_ndims (space)) < 0)errorcall (iinfo->call, "unable to get space rank");{hsize_t dims[rank];hsize_t maxdims[rank];if (H5Sget_simple_extent_dims (space, dims, maxdims) < 0)errorcall (iinfo->call, "unable to get space extent");if (type == VECSXP && rank == 1){unsigned colcount = H5Tget_nmembers (tid), ci;SEXP vec;size_t size = H5Tget_size (tid);unsigned ri, rowcount = dims[0];char buf[rowcount][size];hid_t rtid = make_sexp_ref_type (iinfo->call);SEXP names;if (H5Dread (dataset, tid, space, space, H5P_DEFAULT,buf) < 0)errorcall (iinfo->call, "can't read compound data vector");PROTECT (vec = allocVector (VECSXP, colcount));PROTECT (names = allocVector (STRSXP, colcount));for (ci = 0; ci < colcount; ci++){hid_t ctid = H5Tget_member_type (tid, ci);H5T_class_t class = H5Tget_class (ctid);size_t csize = H5Tget_size (ctid);size_t coffset = H5Tget_member_offset (tid, ci);SEXPREC **rowptr = &VECTOR (vec)[ci];unsigned char itembuf[size]; /* for overrun */#define VECLOOP(vectype, vecref, dtid) \{ \size_t dsize = H5Tget_size (dtid); \for (ri = 0; ri < rowcount; ri++) \{ \memcpy (itembuf, &buf[ri][coffset], csize); \if (H5Tconvert (ctid, dtid, 1, itembuf, NULL, H5P_DEFAULT) < 0) \errorcall (iinfo->call, "type conversion failed"); \memcpy (&vecref (*rowptr)[ri], itembuf, dsize); \} \}{char *colname = H5Tget_member_name (tid, ci);if (colname)STRING (names)[ci] = mkChar (colname);}switch (class) {case H5T_INTEGER:{SEXPTYPE type = (csize == 1) ? LGLSXP : INTSXP;*rowptr = allocVector (type, rowcount);VECLOOP (type, INTEGER, H5T_NATIVE_INT);}break;case H5T_FLOAT:*rowptr = allocVector (REALSXP, rowcount);VECLOOP (REALSXP, REAL, H5T_NATIVE_DOUBLE);break;case H5T_STRING:*rowptr = allocVector (STRSXP, rowcount);VECLOOP (STRSXP, STRING, rtid);break;default:errorcall (iinfo->call, "can't handle hdf class %d",class);}}load_rownames_dataset_attribute (iinfo->call, dataset, vec);setAttrib (vec, R_NamesSymbol, names);UNPROTECT (1);setAttrib (vec, R_ClassSymbol, mkString ("data.frame"));iinfo->add (iinfo, name, vec);hdf5_load_attributes (iinfo->call, dataset, vec, name);UNPROTECT (1);if (H5Tclose (rtid) < 0)errorcall (iinfo->call, "could not close reference type");}else{SEXP obj;PROTECT (obj = ((rank == 1)? allocVector (type, dims[0]): allocMatrix (type, dims[0], dims[1])));vector_io (iinfo->call, FALSE, dataset, space, obj);iinfo->add (iinfo, name, obj);hdf5_load_attributes (iinfo->call, dataset, obj, name);UNPROTECT (1);}}if (H5Sclose (space) < 0)errorcall (iinfo->call, "unable to close dataspace");if (H5Tclose (tid) < 0)errorcall (iinfo->call, "unable to close datatype");if (H5Dclose (dataset) < 0)errorcall (iinfo->call, "unable to close dataset");}elseerrorcall (iinfo->call, "no support for HDF object type: %d",statbuf.type);return 0;}static voidadd_to_symbol_table (struct hdf5_iterate_info *iinfo,const char *name,SEXP obj){setVar (install ((char *)name), obj, iinfo->env);}static voidadd_to_return_list (struct hdf5_iterate_info *iinfo,const char *name,SEXP obj){PROTECT (iinfo->ret);iinfo->ret = CONS (obj, iinfo->ret);TAG (iinfo->ret) = install ((char *) name);UNPROTECT (1);}SEXP do_hdf5load (SEXP call, SEXP op, SEXP args, SEXP env){const char *path;hid_t fid;int restore_syms;struct hdf5_iterate_info iinfo;checkArity (op, args);if (!isValidString(CAR(args)))errorcall (call, "first argument must be a pathname\n");if (TYPEOF (CADR (args)) != LGLSXP)errorcall (call, "second argument must be a logical vector");path = CHAR(STRING (CAR(args))[0]);restore_syms = INTEGER (CADR(args))[0];H5dont_atexit ();if ((fid = H5Fopen (path, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)errorcall (call, "unable to open HDF file: %s", path);if (H5Tregister (H5T_PERS_SOFT,STRING2REF_CONV,H5T_C_S1,H5T_STD_REF_OBJ, string_ref) < 0)errorcall (call, "Unable to register string->ref converter");iinfo.call = call;iinfo.add = restore_syms ? add_to_symbol_table : add_to_return_list;iinfo.env = env;iinfo.ret = R_NilValue;if (H5Giterate (fid, "/", NULL, hdf5_process_object, &iinfo) < 0)errorcall (call, "unable to iterate over HDF file: %s", path);if (H5Tunregister (H5T_PERS_SOFT, STRING2REF_CONV, -1, -1, string_ref) < 0)errorcall (call, "Unable to unregister string->ref converter");if (H5Fclose (fid) < 0)errorcall (call, "unable to close HDF file");return iinfo.ret;}#elseSEXP do_hdf5save (SEXP call, SEXP op, SEXP args, SEXP env){errorcall(call, "HDF5 support unavailable");return(R_NilValue);/* -Wall */}SEXP do_hdf5load (SEXP call, SEXP op, SEXP args, SEXP env){errorcall(call, "HDF5 support unavailable");return(R_NilValue);/* -Wall */}#endif