Make major improvements to stdio

Buffering now has optimal performance, bugs have been fixed, and some
missing apis have been introduced. This implementation is also now more
production worthy since it's less brittle now in terms of system errors.
That's going to help redbean since lua i/o is all based on stdio.

See #97
This commit is contained in:
Justine Tunney 2021-03-26 22:31:41 -07:00
parent 09bcfa23d5
commit da36e7e256
69 changed files with 1595 additions and 735 deletions

View file

@ -17,54 +17,63 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/stdio/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
/**
* Reads string from stream.
*
* @param line is the caller's buffer (in/out) which is extended
* automatically. *line may be NULL but only if *n is 0;
* NUL-termination is guaranteed FTMP
* @param n is the capacity of line (in/out)
* @param s is the caller's buffer (in/out) which is extended or
* allocated automatically, also NUL-terminated is guaranteed
* @param n is the capacity of s (in/out)
* @param delim is the stop char (and NUL is implicitly too)
* @return number of bytes read, including delim, excluding NUL, or -1
* w/ errno on EOF or error; see ferror() and feof()
* @note this function can't punt EINTR to caller
* @see getline(), gettok_r()
*/
ssize_t getdelim(char **line, size_t *n, int delim, FILE *f) {
assert((*line && *n) || (!*line && !*n));
ssize_t rc = -1;
size_t i = 0;
char *p2;
size_t n2;
int c;
for (;;) {
if ((c = getc(f)) == -1) {
if (ferror(f) == EINTR) continue;
if (feof(f) && i) rc = i;
break;
}
if (i + 2 > *n) {
n2 = MAX(11, *n);
n2 += n2 >> 1;
if ((p2 = realloc(*line, n2))) {
*line = p2;
*n = n2;
} else {
__fseterrno(f);
break;
}
}
if (((*line)[i++] = c) == delim) {
rc = i;
ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) {
char *p;
ssize_t rc;
size_t i, m;
if ((f->iomode & O_ACCMODE) == O_WRONLY) {
f->state = errno = EBADF;
return -1;
}
if (f->beg > f->end || f->bufmode == _IONBF) {
f->state = errno = EINVAL;
return -1;
}
if (!*s) *n = 0;
for (i = 0;; i += m) {
m = f->end - f->beg;
if ((p = memchr(f->buf + f->beg, delim, m))) m = p + 1 - (f->buf + f->beg);
if (i + m + 1 > *n && !(*s = realloc(*s, (*n = i + m + 1)))) abort();
memcpy(*s + i, f->buf + f->beg, m);
(*s)[i + m] = '\0';
if ((f->beg += m) == f->end) f->beg = f->end = 0;
if (p) {
return i + m;
} else if (f->fd == -1) {
break;
} else if ((rc = read(f->fd, f->buf, f->size)) != -1) {
if (!rc) break;
f->end = rc;
} else if (errno != EINTR) {
f->state = errno;
return -1;
}
}
if (*line && i < *n) (*line)[i] = '\0';
return rc;
f->state = -1;
if (i + m) {
return i + m;
} else {
return -1;
}
}