libfetch: Reduce copying

Reduce the amount of copying we do when performing buffered reads.

MFC after:	1 week
Reviewed by:	op
Differential Revision:	https://reviews.freebsd.org/D58113
This commit is contained in:
Dag-Erling Smørgrav committed 2026-07-13 08:43:56 +02:00
1 parent d4e0e1fbc2
commit 60382b4a04
4 files changed
+61 -50

No files matched your search

+29 -16
View File
@@ -1437,12 +1437,23 @@ fetch_getln(conn_t *conn)
/* allocate initial buffer */
if (conn->buf == NULL) {
if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL)
return (-1);
goto fail;
conn->line = conn->buf;
conn->bufsize = MIN_BUF_SIZE;
conn->buflen = 0;
conn->pos = 0;
}
/* look at the data we already have */
if (conn->pos < conn->buflen) {
conn->line = conn->buf + conn->pos;
while (conn->pos < conn->buflen)
if (conn->buf[conn->pos++] == '\n')
goto found;
/* reset for the upcoming memmove() */
conn->pos = conn->line - conn->buf;
}
/* move residual data up */
if (conn->buflen > 0) {
if (conn->pos < conn->buflen) {
@@ -1451,20 +1462,16 @@ fetch_getln(conn_t *conn)
}
conn->buflen -= conn->pos;
conn->pos -= conn->pos;
conn->line = conn->buf;
}
/* do we have a complete line? */
while (conn->pos < conn->buflen)
if (conn->buf[conn->pos++] == '\n')
goto found;
for (;;) {
/* read as much as we can right now */
rlen = fetch_read(conn, conn->buf + conn->buflen,
conn->bufsize - conn->buflen - 1);
/* error */
if (rlen < 0)
return (-1);
goto fail;
/* advance and terminate */
conn->buflen += rlen;
conn->buf[conn->buflen] = '\0';
@@ -1480,21 +1487,25 @@ fetch_getln(conn_t *conn)
tmp = conn->buf;
tmpsize = conn->bufsize * 2;
if ((tmp = realloc(tmp, tmpsize)) == NULL)
return (-1);
conn->buf = tmp;
goto fail;
conn->line = conn->buf = tmp;
conn->bufsize = tmpsize;
}
}
/* connection closed, return what's left */
conn->pos = conn->buflen;
found:
rlen = conn->pos;
if (rlen > 0 && conn->buf[rlen - 1] == '\n')
conn->buf[--rlen] = '\0';
if (rlen > 0 && conn->buf[rlen - 1] == '\r')
conn->buf[--rlen] = '\0';
DEBUGF("<<< %.*s\n", (int)rlen, conn->buf);
return (rlen);
conn->linelen = (conn->buf + conn->pos) - conn->line;
if (conn->linelen > 0 && conn->line[conn->linelen - 1] == '\n')
conn->line[--conn->linelen] = '\0';
if (conn->linelen > 0 && conn->line[conn->linelen - 1] == '\r')
conn->line[--conn->linelen] = '\0';
DEBUGF("<<< %.*s\n", (int)conn->linelen, conn->line);
return (conn->linelen);
fail:
conn->line = NULL;
conn->linelen = 0;
return (-1);
}
@@ -1520,6 +1531,8 @@ fetch_bufread(conn_t *conn, void *buf, size_t len)
conn->buflen = 0;
conn->pos = 0;
}
conn->line = NULL;
conn->linelen = 0;
/* return residual data first */
if (conn->buflen > conn->pos) {
+2
View File
@@ -49,8 +49,10 @@ typedef struct fetchconn conn_t;
struct fetchconn {
int sd; /* socket descriptor */
char *buf; /* buffer */
char *line; /* line (within buffer) */
size_t bufsize; /* buffer size */
size_t buflen; /* length of buffer contents */
size_t linelen; /* length of line */
size_t pos; /* current position in buffer */
int err; /* last protocol reply code */
#ifdef WITH_SSL
+12 -11
View File
@@ -148,8 +148,9 @@ ftp_chkerr(conn_t *conn)
fetch_syserr();
return (-1);
}
if (isftpinfo(conn->buf)) {
while (conn->buflen && !isftpreply(conn->buf)) {
if (isftpinfo(conn->line)) {
while (rlen && !isftpreply(conn->line)) {
if ((rlen = fetch_getln(conn)) < 0) {
fetch_syserr();
return (-1);
@@ -157,14 +158,14 @@ ftp_chkerr(conn_t *conn)
}
}
if (!isftpreply(conn->buf)) {
if (!isftpreply(conn->line)) {
ftp_seterr(FTP_PROTOCOL_ERROR);
return (-1);
}
conn->err = (conn->buf[0] - '0') * 100
+ (conn->buf[1] - '0') * 10
+ (conn->buf[2] - '0');
conn->err = (conn->line[0] - '0') * 100
+ (conn->line[1] - '0') * 10
+ (conn->line[2] - '0');
return (conn->err);
}
@@ -236,8 +237,8 @@ ftp_pwd(conn_t *conn, char *pwd, size_t pwdlen)
if (conn->err != FTP_WORKING_DIRECTORY &&
conn->err != FTP_FILE_ACTION_OK)
return (FTP_PROTOCOL_ERROR);
end = conn->buf + conn->buflen;
src = conn->buf + 4;
end = conn->line + conn->linelen;
src = conn->line + 4;
if (src >= end || *src++ != '"')
return (FTP_PROTOCOL_ERROR);
for (q = 0, dst = pwd; src < end && pwdlen--; ++src) {
@@ -417,7 +418,7 @@ ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
ftp_seterr(e);
return (-1);
}
for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
for (ln = conn->line + 4; *ln && isspace((unsigned char)*ln); ln++)
/* nothing */ ;
for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++)
us->size = us->size * 10 + *ln - '0';
@@ -435,7 +436,7 @@ ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
ftp_seterr(e);
return (-1);
}
for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
for (ln = conn->line + 4; *ln && isspace((unsigned char)*ln); ln++)
/* nothing */ ;
switch (strspn(ln, "0123456789")) {
case 14:
@@ -688,7 +689,7 @@ ftp_transfer(conn_t *conn, const char *oper, const char *file,
* Find address and port number. The reply to the PASV command
* is IMHO the one and only weak point in the FTP protocol.
*/
ln = conn->buf;
ln = conn->line;
switch (e) {
case FTP_PASSIVE_MODE:
case FTP_LPASSIVE_MODE:
+18 -23
View File
@@ -126,8 +126,7 @@
* I/O functions for decoding chunked streams
*/
struct httpio
{
struct httpio {
conn_t *conn; /* connection */
int chunked; /* chunked mode */
char *buf; /* chunk buffer */
@@ -148,17 +147,14 @@ struct httpio
static int
http_new_chunk(struct httpio *io)
{
unsigned char *p, *eol;
char *p;
if (fetch_getln(io->conn) == -1)
return (-1);
p = (unsigned char *)io->conn->buf;
if (io->conn->pos < 2 || !isxdigit(*p))
return (-1);
eol = (unsigned char *)io->conn->buf + io->conn->pos;
while (p < eol && *p && !isspace(*p) && *p != ';') {
for (p = io->conn->line;
*p != '\0' && !isspace((unsigned char)*p) && *p != ';';
p++) {
io->chunksize <<= 4;
if (*p >= '0' && *p <= '9')
io->chunksize += *p - '0';
@@ -168,7 +164,6 @@ http_new_chunk(struct httpio *io)
io->chunksize += 10 + *p - 'a';
else
return (-1);
p++;
}
#ifndef NDEBUG
@@ -435,9 +430,9 @@ http_get_reply(conn_t *conn)
* on finding one, but if we do, insist on it being 1.0 or 1.1.
* We don't care about the reason phrase.
*/
if (strncmp(conn->buf, "HTTP", 4) != 0)
if (conn->linelen < 4 || strncmp(conn->line, "HTTP", 4) != 0)
return (HTTP_PROTOCOL_ERROR);
p = conn->buf + 4;
p = conn->line + 4;
if (*p == '/') {
if (p[1] != '1' || p[2] != '.' || (p[3] != '0' && p[3] != '1'))
return (HTTP_PROTOCOL_ERROR);
@@ -521,17 +516,17 @@ http_next_header(conn_t *conn, http_headerbuf_t *hbuf, const char **p)
{
unsigned int i, len;
if (conn->pos == 0 || conn->buf[0] == '\0')
if (conn->linelen == 0)
return (hdr_end);
/* Copy the line to the headerbuf */
if (hbuf->bufsize < conn->pos + 1) {
if ((hbuf->buf = realloc(hbuf->buf, conn->pos + 1)) == NULL)
if (hbuf->bufsize <= conn->linelen) {
if ((hbuf->buf = realloc(hbuf->buf, conn->linelen + 1)) == NULL)
return (hdr_syserror);
hbuf->bufsize = conn->pos + 1;
hbuf->bufsize = conn->linelen + 1;
}
strcpy(hbuf->buf, conn->buf);
hbuf->buflen = conn->buflen;
memcpy(hbuf->buf, conn->line, conn->linelen + 1);
hbuf->buflen = conn->linelen;
/*
* Fetch possible continuation lines. Stop at 1st non-continuation
@@ -545,19 +540,19 @@ http_next_header(conn_t *conn, http_headerbuf_t *hbuf, const char **p)
* Note: we previously considered a pure whitespace line
* equivalent to an empty one. This was incorrect.
*/
if (conn->buf[0] != ' ' && conn->buf[0] != "\t"[0])
if (conn->line[0] != ' ' && conn->line[0] != '\t')
break;
/* Got a continuation line. Concatenate to previous */
len = hbuf->buflen + conn->buflen;
if (hbuf->bufsize < len + 1) {
len = hbuf->buflen + conn->linelen;
if (hbuf->bufsize <= len) {
len *= 2;
if ((hbuf->buf = realloc(hbuf->buf, len + 1)) == NULL)
return (hdr_syserror);
hbuf->bufsize = len + 1;
}
strcpy(hbuf->buf + hbuf->buflen, conn->buf);
hbuf->buflen += conn->buflen;
memcpy(hbuf->buf + hbuf->buflen, conn->line, conn->linelen + 1);
hbuf->buflen += conn->linelen;
}
/*