I've just implemented a generalization of R's text connections, to
also support reading/writing raw binary data. There is very little
new code to speak of. For input connections, I wrote code to populate
the old text connection buffer from a raw vector, and provided a new
raw_read() method. For output connections, I wrote a raw_write() to
append to a raw vector. On input, the mode (text or binary) is
determined by the data type of the input object; on output, I use the
requested output mode (i.e. "w" / "wb"). For example:
> con <- loopConnection("r", "wb")
> a <- c(10,100,1000)
> writeBin(a, con, size=4)
> r
[1] 00 00 20 41 00 00 c8 42 00 00 7a 44
> close(con)
> con <- loopConnection(r)
> readBin(con, "double", n=3, size=4)
[1] 10 100 1000
> close(con)
I think "loop connection" is a better name for this sort of connection
than "text connection" was even for the old version; that confuses the
mode of the connection (text vs binary) with the mechanism (file,
socket, etc).
I've appended a patch to the end of this message. As implemented
here, textConnection is replaced by loopConnection but functionally
this is a superset of the old textConnection. For compatibility, one
could add:
textConnection <- function(...) loopConnection(...)
The patch is against R-2.1.1. I can investigate whether any changes
are required for the current development tree. I can also update the
documentation files as required. I thought I'd first check whether
anyone else thought this was worth inclusion before spending more time
on it.
The raw_write() code could be improved with smarter memory allocation
(grabbing bigger chunks rather than reallocating the raw vector for
every write), but this is at least a proof of principle.
-- David Hinds
--- src/main/connections.c.orig 2005-06-17 19:05:02.000000000 -0700
+++ src/main/connections.c 2005-08-22 15:54:03.156038200 -0700
@@ -1644,13 +1644,13 @@
return ans;
}
-/* ------------------- text connections --------------------- */
+/* ------------------- loop connections --------------------- */
/* read a R character vector into a buffer */
static void text_init(Rconnection con, SEXP text)
{
int i, nlines = length(text), nchars = 0;
- Rtextconn this = (Rtextconn)con->private;
+ Rloopconn this = (Rloopconn)con->private;
for(i = 0; i < nlines; i++)
nchars += strlen(CHAR(STRING_ELT(text, i))) + 1;
@@ -1668,19 +1668,35 @@
this->cur = this->save = 0;
}
-static Rboolean text_open(Rconnection con)
+/* read a R raw vector into a buffer */
+static void raw_init(Rconnection con, SEXP raw)
+{
+ int nbytes = length(raw);
+ Rloopconn this = (Rloopconn)con->private;
+
+ this->data = (char *) malloc(nbytes);
+ if(!this->data) {
+ free(this); free(con->description); free(con->class); free(con);
+ error(_("cannot allocate memory for raw connection"));
+ }
+ memcpy(this->data, RAW(raw), nbytes);
+ this->nchars = nbytes;
+ this->cur = this->save = 0;
+}
+
+static Rboolean loop_open(Rconnection con)
{
con->save = -1000;
return TRUE;
}
-static void text_close(Rconnection con)
+static void loop_close(Rconnection con)
{
}
-static void text_destroy(Rconnection con)
+static void loop_destroy(Rconnection con)
{
- Rtextconn this = (Rtextconn)con->private;
+ Rloopconn this = (Rloopconn)con->private;
free(this->data);
/* this->cur = this->nchars = 0; */
@@ -1689,7 +1705,7 @@
static int text_fgetc(Rconnection con)
{
- Rtextconn this = (Rtextconn)con->private;
+ Rloopconn this = (Rloopconn)con->private;
if(this->save) {
int c;
c = this->save;
@@ -1700,48 +1716,69 @@
else return (int) (this->data[this->cur++]);
}
-static double text_seek(Rconnection con, double where, int origin, int rw)
+static double loop_seek(Rconnection con, double where, int origin, int rw)
{
- if(where >= 0) error(_("seek is not relevant for text
connection"));
+ if(where >= 0) error(_("seek is not relevant for loop
connection"));
return 0; /* if just asking, always at the beginning */
}
-static Rconnection newtext(char *description, SEXP text)
+static size_t raw_read(void *ptr, size_t size, size_t nitems,
+ Rconnection con)
+{
+ Rloopconn this = (Rloopconn)con->private;
+ if (this->cur + size*nitems > this->nchars) {
+ nitems = (this->nchars - this->cur)/size;
+ memcpy(ptr, this->data+this->cur, size*nitems);
+ this->cur = this->nchars;
+ } else {
+ memcpy(ptr, this->data+this->cur, size*nitems);
+ this->cur += size*nitems;
+ }
+ return nitems;
+}
+
+static Rconnection newloop(char *description, SEXP data)
{
Rconnection new;
new = (Rconnection) malloc(sizeof(struct Rconn));
- if(!new) error(_("allocation of text connection failed"));
- new->class = (char *) malloc(strlen("textConnection") + 1);
+ if(!new) error(_("allocation of loop connection failed"));
+ new->class = (char *) malloc(strlen("loopConnection") + 1);
if(!new->class) {
free(new);
- error(_("allocation of text connection failed"));
+ error(_("allocation of loop connection failed"));
}
- strcpy(new->class, "textConnection");
+ strcpy(new->class, "loopConnection");
new->description = (char *) malloc(strlen(description) + 1);
if(!new->description) {
free(new->class); free(new);
- error(_("allocation of text connection failed"));
+ error(_("allocation of loop connection failed"));
}
init_con(new, description, "r");
new->isopen = TRUE;
new->canwrite = FALSE;
- new->open = &text_open;
- new->close = &text_close;
- new->destroy = &text_destroy;
- new->fgetc = &text_fgetc;
- new->seek = &text_seek;
- new->private = (void*) malloc(sizeof(struct textconn));
+ new->open = &loop_open;
+ new->close = &loop_close;
+ new->destroy = &loop_destroy;
+ new->seek = &loop_seek;
+ new->private = (void*) malloc(sizeof(struct loopconn));
if(!new->private) {
free(new->description); free(new->class); free(new);
- error(_("allocation of text connection failed"));
+ error(_("allocation of loop connection failed"));
+ }
+ new->text = isString(data);
+ if (new->text) {
+ new->fgetc = &text_fgetc;
+ text_init(new, data);
+ } else {
+ new->read = &raw_read;
+ raw_init(new, data);
}
- text_init(new, text);
return new;
}
-static void outtext_close(Rconnection con)
+static void outloop_close(Rconnection con)
{
- Routtextconn this = (Routtextconn)con->private;
+ Routloopconn this = (Routloopconn)con->private;
SEXP tmp;
int idx = ConnIndex(con);
@@ -1755,9 +1792,9 @@
SET_VECTOR_ELT(OutTextData, idx, R_NilValue);
}
-static void outtext_destroy(Rconnection con)
+static void outloop_destroy(Rconnection con)
{
- Routtextconn this = (Routtextconn)con->private;
+ Routloopconn this = (Routloopconn)con->private;
free(this->lastline); free(this);
}
@@ -1765,7 +1802,7 @@
static int text_vfprintf(Rconnection con, const char *format, va_list ap)
{
- Routtextconn this = (Routtextconn)con->private;
+ Routloopconn this = (Routloopconn)con->private;
char buf[BUFSIZE], *b = buf, *p, *q, *vmax = vmaxget();
int res = 0, usedRalloc = FALSE, buffree,
already = strlen(this->lastline);
@@ -1830,24 +1867,41 @@
return res;
}
-static void outtext_init(Rconnection con, char *mode, int idx)
+static size_t raw_write(const void *ptr, size_t size, size_t nitems,
+ Rconnection con)
+{
+ Routloopconn this = (Routloopconn)con->private;
+ SEXP tmp;
+ int idx = ConnIndex(con);
+
+ PROTECT(tmp = lengthgets(this->data, this->len + size*nitems));
+ memcpy(RAW(tmp)+this->len, ptr, size*nitems);
+ this->len += size*nitems;
+ defineVar(this->namesymbol, tmp, VECTOR_ELT(OutTextData, idx));
+ this->data = tmp;
+ UNPROTECT(1);
+ return nitems;
+}
+
+static void outloop_init(Rconnection con, char *mode, int idx)
{
- Routtextconn this = (Routtextconn)con->private;
+ Routloopconn this = (Routloopconn)con->private;
+ int st = (con->text ? STRSXP : RAWSXP);
SEXP val;
this->namesymbol = install(con->description);
- if(strcmp(mode, "w") == 0) {
+ if(strncmp(mode, "w", 1) == 0) {
/* create variable pointed to by con->description */
- PROTECT(val = allocVector(STRSXP, 0));
+ PROTECT(val = allocVector(st, 0));
defineVar(this->namesymbol, val, VECTOR_ELT(OutTextData, idx));
UNPROTECT(1);
} else {
/* take over existing variable */
val = findVar1(this->namesymbol, VECTOR_ELT(OutTextData, idx),
- STRSXP, FALSE);
+ st, FALSE);
if(val == R_UnboundValue) {
- warning(_("text connection: appending to a non-existent char
vector"));
- PROTECT(val = allocVector(STRSXP, 0));
+ warning(_("loop connection: appending to a non-existent
vector"));
+ PROTECT(val = allocVector(st, 0));
defineVar(this->namesymbol, val, VECTOR_ELT(OutTextData, idx));
UNPROTECT(1);
}
@@ -1859,49 +1913,55 @@
}
-static Rconnection newouttext(char *description, SEXP sfile, char *mode,
+static Rconnection newoutloop(char *description, SEXP sfile, char *mode,
int idx)
{
+ int isText = (mode[1] != 'b');
Rconnection new;
void *tmp;
new = (Rconnection) malloc(sizeof(struct Rconn));
- if(!new) error(_("allocation of text connection failed"));
- new->class = (char *) malloc(strlen("textConnection") + 1);
+ if(!new) error(_("allocation of loop connection failed"));
+ new->class = (char *) malloc(strlen("loopConnection") + 1);
if(!new->class) {
free(new);
- error(_("allocation of text connection failed"));
+ error(_("allocation of loop connection failed"));
}
- strcpy(new->class, "textConnection");
+ strcpy(new->class, "loopConnection");
new->description = (char *) malloc(strlen(description) + 1);
if(!new->description) {
free(new->class); free(new);
- error(_("allocation of text connection failed"));
+ error(_("allocation of loop connection failed"));
}
init_con(new, description, mode);
+ new->text = isText;
new->isopen = TRUE;
new->canread = FALSE;
- new->open = &text_open;
- new->close = &outtext_close;
- new->destroy = &outtext_destroy;
- new->vfprintf = &text_vfprintf;
- new->seek = &text_seek;
- new->private = (void*) malloc(sizeof(struct outtextconn));
+ new->open = &loop_open;
+ new->close = &outloop_close;
+ new->destroy = &outloop_destroy;
+ new->seek = &loop_seek;
+ new->private = (void*) malloc(sizeof(struct outloopconn));
if(!new->private) {
free(new->description); free(new->class); free(new);
- error(_("allocation of text connection failed"));
+ error(_("allocation of loop connection failed"));
}
- ((Routtextconn)new->private)->lastline = tmp = malloc(LAST_LINE_LEN);
+ ((Routloopconn)new->private)->lastline = tmp = malloc(LAST_LINE_LEN);
if(!tmp) {
free(new->private);
free(new->description); free(new->class); free(new);
- error(_("allocation of text connection failed"));
+ error(_("allocation of loop connection failed"));
}
- outtext_init(new, mode, idx);
+ if (isText) {
+ new->vfprintf = &text_vfprintf;
+ } else {
+ new->write = &raw_write;
+ }
+ outloop_init(new, mode, idx);
return new;
}
-SEXP do_textconnection(SEXP call, SEXP op, SEXP args, SEXP env)
+SEXP do_loopconnection(SEXP call, SEXP op, SEXP args, SEXP env)
{
SEXP sfile, stext, sopen, ans, class, venv;
char *desc, *open;
@@ -1914,8 +1974,6 @@
error(_("invalid 'description' argument"));
desc = CHAR(STRING_ELT(sfile, 0));
stext = CADR(args);
- if(!isString(stext))
- error(_("invalid 'text' argument"));
sopen = CADDR(args);
if(!isString(sopen) || length(sopen) != 1)
error(_("invalid 'open' argument"));
@@ -1924,16 +1982,20 @@
if (!isEnvironment(venv) && venv != R_NilValue)
error(_("invalid 'environment' argument"));
ncon = NextConnection();
- if(!strlen(open) || strncmp(open, "r", 1) == 0)
- con = Connections[ncon] = newtext(desc, stext);
- else if (strncmp(open, "w", 1) == 0 || strncmp(open,
"a", 1) == 0) {
+ if(!strlen(open) || strncmp(open, "r", 1) == 0) {
+ if(!isString(stext) && (TYPEOF(stext) != RAWSXP))
+ error(_("invalid 'object' argument"));
+ con = Connections[ncon] = newloop(desc, stext);
+ } else if (strncmp(open, "w", 1) == 0 || strncmp(open,
"a", 1) == 0) {
+ if(!isString(stext))
+ error(_("invalid 'object' argument"));
if (OutTextData == NULL) {
OutTextData = allocVector(VECSXP, NCONNECTIONS);
R_PreserveObject(OutTextData);
}
SET_VECTOR_ELT(OutTextData, ncon, venv);
con = Connections[ncon] - newouttext(CHAR(STRING_ELT(stext, 0)), sfile,
open, ncon);
+ newoutloop(CHAR(STRING_ELT(stext, 0)), sfile, open, ncon);
}
else
errorcall(call, _("unsupported mode"));
@@ -1942,7 +2004,7 @@
PROTECT(ans = allocVector(INTSXP, 1));
INTEGER(ans)[0] = ncon;
PROTECT(class = allocVector(STRSXP, 2));
- SET_STRING_ELT(class, 0, mkChar("textConnection"));
+ SET_STRING_ELT(class, 0, mkChar("loopConnection"));
SET_STRING_ELT(class, 1, mkChar("connection"));
classgets(ans, class);
UNPROTECT(2);
--- src/main/names.c.orig 2005-05-20 05:51:46.000000000 -0700
+++ src/main/names.c 2005-08-22 15:59:47.968828400 -0700
@@ -866,7 +866,7 @@
{"pushBack", do_pushback, 0, 11, 3, {PP_FUNCALL,
PREC_FN, 0}},
{"clearPushBackLength",do_clearpushback,0, 11, 1,
{PP_FUNCALL, PREC_FN, 0}},
{"pushBackLength",do_pushbacklength,0, 11, 1, {PP_FUNCALL,
PREC_FN, 0}},
-{"textConnection",do_textconnection,0, 11, 4, {PP_FUNCALL,
PREC_FN, 0}},
+{"loopConnection",do_loopconnection,0, 11, 4, {PP_FUNCALL,
PREC_FN, 0}},
{"socketConnection",do_sockconn,0, 11, 6, {PP_FUNCALL,
PREC_FN, 0}},
{"sockSelect",do_sockselect,0, 11, 3, {PP_FUNCALL, PREC_FN,
0}},
{"getAllConnections",do_getallconnections,0,11, 0, {PP_FUNCALL,
PREC_FN, 0}},
--- src/include/Rconnections.h.orig 2005-04-18 04:34:02.000000000 -0700
+++ src/include/Rconnections.h 2005-08-22 15:40:02.582767400 -0700
@@ -82,19 +82,19 @@
int cp;
} *Rgzfileconn;
-typedef struct textconn {
+typedef struct loopconn {
char *data; /* all the data */
int cur, nchars; /* current pos and number of chars */
char save; /* pushback */
-} *Rtextconn;
+} *Rloopconn;
-typedef struct outtextconn {
+typedef struct outloopconn {
int len; /* number of lines */
SEXP namesymbol;
SEXP data;
char *lastline;
int lastlinelength; /* buffer size */
-} *Routtextconn;
+} *Routloopconn;
typedef enum {HTTPsh, FTPsh} UrlScheme;
--- src/library/base/R/connections.R.orig 2005-04-18 04:34:17.000000000 -0700
+++ src/library/base/R/connections.R 2005-08-22 16:18:22.095231400 -0700
@@ -83,10 +83,10 @@
encoding = getOption("encoding"))
.Internal(socketConnection(host, port, server, blocking, open, encoding))
-textConnection <- function(object, open = "r", local = FALSE) {
+loopConnection <- function(object, open = "r", local = FALSE) {
if (local) env <- parent.frame()
else env <- .GlobalEnv
- .Internal(textConnection(deparse(substitute(object)), object, open, env))
+ .Internal(loopConnection(deparse(substitute(object)), object, open, env))
}
seek <- function(con, ...)