summaryrefslogtreecommitdiffstats
path: root/libc/stdio
diff options
context:
space:
mode:
Diffstat (limited to 'libc/stdio')
-rw-r--r--libc/stdio/fileext.h20
-rw-r--r--libc/stdio/findfp.c8
-rw-r--r--libc/stdio/fread.c105
-rw-r--r--libc/stdio/local.h6
-rw-r--r--libc/stdio/stdio.c90
-rw-r--r--libc/stdio/stdio_ext.cpp101
6 files changed, 282 insertions, 48 deletions
diff --git a/libc/stdio/fileext.h b/libc/stdio/fileext.h
index c074b4b..6cacc0f 100644
--- a/libc/stdio/fileext.h
+++ b/libc/stdio/fileext.h
@@ -33,6 +33,7 @@
#define _FILEEXT_H_
#include <pthread.h>
+#include <stdbool.h>
__BEGIN_DECLS
@@ -40,14 +41,18 @@ __BEGIN_DECLS
* file extension
*/
struct __sfileext {
- struct __sbuf _ub; /* ungetc buffer */
+ struct __sbuf _ub; /* ungetc buffer */
struct wchar_io_data _wcio; /* wide char io status */
- pthread_mutex_t _lock; /* file lock */
+ pthread_mutex_t _lock; /* file lock */
+ bool _stdio_handles_locking; /* __fsetlocking support */
};
-#define _FILEEXT_INITIALIZER {{NULL,0},{0},PTHREAD_RECURSIVE_MUTEX_INITIALIZER}
-
+#if defined(__cplusplus)
+#define _EXT(fp) reinterpret_cast<__sfileext*>((fp)->_ext._base)
+#else
#define _EXT(fp) ((struct __sfileext *)((fp)->_ext._base))
+#endif
+
#define _UB(fp) _EXT(fp)->_ub
#define _FLOCK(fp) _EXT(fp)->_lock
@@ -56,7 +61,12 @@ do { \
_UB(fp)._base = NULL; \
_UB(fp)._size = 0; \
WCIO_INIT(fp); \
- _FLOCK(fp).value = __PTHREAD_RECURSIVE_MUTEX_INIT_VALUE; \
+ pthread_mutexattr_t attr; \
+ pthread_mutexattr_init(&attr); \
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
+ pthread_mutex_init(&_FLOCK(fp), &attr); \
+ pthread_mutexattr_destroy(&attr); \
+ _EXT(fp)->_stdio_handles_locking = true; \
} while (0)
#define _FILEEXT_SETUP(f, fext) \
diff --git a/libc/stdio/findfp.c b/libc/stdio/findfp.c
index cfbb66b..5e51198 100644
--- a/libc/stdio/findfp.c
+++ b/libc/stdio/findfp.c
@@ -44,17 +44,13 @@
#define ALIGNBYTES (sizeof(uintptr_t) - 1)
#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
-#undef stdin
-#undef stdout
-#undef stderr
-
int __sdidinit;
#define NDYNAMIC 10 /* add ten more whenever necessary */
#define std(flags, file) \
- {0,0,0,flags,file,{0},0,__sF+file,__sclose,__sread,__sseek,__swrite, \
- {(unsigned char *)(__sFext+file), 0},NULL,0,{0},{0},{0},0,0}
+ {0,0,0,flags,file,{0,0},0,__sF+file,__sclose,__sread,__sseek,__swrite, \
+ {(unsigned char *)(__sFext+file), 0},NULL,0,{0},{0},{0,0},0,0}
/* the usual - (stdin + stdout + stderr) */
static FILE usual[FOPEN_MAX - 3];
diff --git a/libc/stdio/fread.c b/libc/stdio/fread.c
index e052128..f3f0127 100644
--- a/libc/stdio/fread.c
+++ b/libc/stdio/fread.c
@@ -35,6 +35,7 @@
#include <string.h>
#include <stdint.h>
#include <errno.h>
+#include <sys/param.h>
#include "local.h"
#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4))
@@ -42,13 +43,8 @@
size_t
fread(void *buf, size_t size, size_t count, FILE *fp)
{
- size_t resid;
- char *p;
- int r;
- size_t total;
-
/*
- * Extension: Catch integer overflow
+ * Extension: Catch integer overflow.
*/
if ((size >= MUL_NO_OVERFLOW || count >= MUL_NO_OVERFLOW) &&
size > 0 && SIZE_MAX / size < count) {
@@ -57,48 +53,87 @@ fread(void *buf, size_t size, size_t count, FILE *fp)
return (0);
}
+ const size_t desired_total = count * size;
+ size_t total = desired_total;
+
/*
* ANSI and SUSv2 require a return value of 0 if size or count are 0.
*/
- if ((resid = count * size) == 0)
+ if (total == 0) {
return (0);
+ }
+
FLOCKFILE(fp);
_SET_ORIENTATION(fp, -1);
+
+ // TODO: how can this ever happen?!
if (fp->_r < 0)
fp->_r = 0;
- total = resid;
- p = buf;
- // BEGIN android-added
- // Avoid pathological behavior on unbuffered files. OpenBSD
- // will loop reading one byte then memcpying one byte!
- if ((fp->_flags & __SNBF) != 0) {
- // We know if we're unbuffered that our buffer is empty, so
- // we can just read directly.
- while (resid > 0 && (r = (*fp->_read)(fp->_cookie, p, resid)) > 0) {
- p += r;
- resid -= r;
- }
- FUNLOCKFILE(fp);
- return ((total - resid) / size);
+ /*
+ * Ensure _bf._size is valid.
+ */
+ if (fp->_bf._base == NULL) {
+ __smakebuf(fp);
}
- // END android-added
- while (resid > (size_t)(r = fp->_r)) {
- (void)memcpy((void *)p, (void *)fp->_p, (size_t)r);
- fp->_p += r;
- /* fp->_r = 0 ... done in __srefill */
- p += r;
- resid -= r;
+ char* dst = buf;
+
+ while (total > 0) {
+ /*
+ * Copy data out of the buffer.
+ */
+ size_t buffered_bytes = MIN((size_t) fp->_r, total);
+ memcpy(dst, fp->_p, buffered_bytes);
+ fp->_p += buffered_bytes;
+ fp->_r -= buffered_bytes;
+ dst += buffered_bytes;
+ total -= buffered_bytes;
+
+ /*
+ * Are we done?
+ */
+ if (total == 0) {
+ goto out;
+ }
+
+ /*
+ * Do we have so much more to read that we should
+ * avoid copying it through the buffer?
+ */
+ if (total > (size_t) fp->_bf._size) {
+ /*
+ * Make sure that fseek doesn't think it can
+ * reuse the buffer since we are going to read
+ * directly from the file descriptor.
+ */
+ fp->_flags |= __SMOD;
+ break;
+ }
+
+ /*
+ * Less than a buffer to go, so refill the buffer and
+ * go around the loop again.
+ */
if (__srefill(fp)) {
- /* no more input: return partial result */
- FUNLOCKFILE(fp);
- return ((total - resid) / size);
+ goto out;
+ }
+ }
+
+ /*
+ * Read directly into the caller's buffer.
+ */
+ while (total > 0) {
+ ssize_t bytes_read = (*fp->_read)(fp->_cookie, dst, total);
+ if (bytes_read <= 0) {
+ fp->_flags |= (bytes_read == 0) ? __SEOF : __SERR;
+ break;
}
+ dst += bytes_read;
+ total -= bytes_read;
}
- (void)memcpy((void *)p, (void *)fp->_p, resid);
- fp->_r -= resid;
- fp->_p += resid;
+
+out:
FUNLOCKFILE(fp);
- return (count);
+ return ((desired_total - total) / size);
}
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index 46b11f1..ce04141 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -111,8 +111,8 @@ extern void __atexit_register_cleanup(void (*)(void));
(fp)->_lb._base = NULL; \
}
-#define FLOCKFILE(fp) flockfile(fp)
-#define FUNLOCKFILE(fp) funlockfile(fp)
+#define FLOCKFILE(fp) if (_EXT(fp)->_stdio_handles_locking) flockfile(fp)
+#define FUNLOCKFILE(fp) if (_EXT(fp)->_stdio_handles_locking) funlockfile(fp)
#define FLOATING_POINT
#define PRINTF_WIDE_CHAR
@@ -124,6 +124,7 @@ extern void __atexit_register_cleanup(void (*)(void));
#define __sferror(p) (((p)->_flags & __SERR) != 0)
#define __sclearerr(p) ((void)((p)->_flags &= ~(__SERR|__SEOF)))
#define __sfileno(p) ((p)->_file)
+#if !defined(__cplusplus)
#define __sgetc(p) (--(p)->_r < 0 ? __srget(p) : (int)(*(p)->_p++))
static __inline int __sputc(int _c, FILE* _p) {
if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n')) {
@@ -132,6 +133,7 @@ static __inline int __sputc(int _c, FILE* _p) {
return (__swbuf(_c, _p));
}
}
+#endif
/* OpenBSD declares these in fvwrite.h but we want to ensure they're hidden. */
struct __suio;
diff --git a/libc/stdio/stdio.c b/libc/stdio/stdio.c
new file mode 100644
index 0000000..13b9887
--- /dev/null
+++ b/libc/stdio/stdio.c
@@ -0,0 +1,90 @@
+/* $OpenBSD: stdio.c,v 1.9 2005/08/08 08:05:36 espie Exp $ */
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "local.h"
+
+/*
+ * Small standard I/O/seek/close functions.
+ * These maintain the `known seek offset' for seek optimisation.
+ */
+int
+__sread(void *cookie, char *buf, int n)
+{
+ FILE *fp = cookie;
+ int ret;
+
+ ret = TEMP_FAILURE_RETRY(read(fp->_file, buf, n));
+ /* if the read succeeded, update the current offset */
+ if (ret >= 0)
+ fp->_offset += ret;
+ else
+ fp->_flags &= ~__SOFF; /* paranoia */
+ return (ret);
+}
+
+int
+__swrite(void *cookie, const char *buf, int n)
+{
+ FILE *fp = cookie;
+
+ if (fp->_flags & __SAPP)
+ (void) TEMP_FAILURE_RETRY(lseek(fp->_file, (off_t)0, SEEK_END));
+ fp->_flags &= ~__SOFF; /* in case FAPPEND mode is set */
+ return TEMP_FAILURE_RETRY(write(fp->_file, buf, n));
+}
+
+fpos_t
+__sseek(void *cookie, fpos_t offset, int whence)
+{
+ FILE *fp = cookie;
+ off_t ret;
+
+ ret = TEMP_FAILURE_RETRY(lseek(fp->_file, (off_t)offset, whence));
+ if (ret == (off_t)-1)
+ fp->_flags &= ~__SOFF;
+ else {
+ fp->_flags |= __SOFF;
+ fp->_offset = ret;
+ }
+ return (ret);
+}
+
+int
+__sclose(void *cookie)
+{
+ return TEMP_FAILURE_RETRY(close(((FILE *)cookie)->_file));
+}
diff --git a/libc/stdio/stdio_ext.cpp b/libc/stdio/stdio_ext.cpp
new file mode 100644
index 0000000..fea44f6
--- /dev/null
+++ b/libc/stdio/stdio_ext.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio_ext.h>
+#include <stdlib.h>
+
+#include "local.h"
+#include "private/libc_logging.h"
+
+size_t __fbufsize(FILE* fp) {
+ return fp->_bf._size;
+}
+
+/* For a _SRW stream, we don't know whether we last read or wrote.
+int __freading(FILE* fp) {
+ return (fp->_flags & _SRD) != 0 || ...;
+}
+*/
+
+/* For a _SRW stream, we don't know whether we last read or wrote.
+int __fwriting(FILE*) {
+ return (fp->_flags & _SWR) != 0 || ...;
+}
+*/
+
+int __freadable(FILE* fp) {
+ return (fp->_flags & (__SRD|__SRW)) != 0;
+}
+
+int __fwritable(FILE* fp) {
+ return (fp->_flags & (__SWR|__SRW)) != 0;
+}
+
+int __flbf(FILE* fp) {
+ return (fp->_flags & __SLBF) != 0;
+}
+
+void __fpurge(FILE* fp) {
+ fpurge(fp);
+}
+
+size_t __fpending(FILE* fp) {
+ return fp->_p - fp->_bf._base;
+}
+
+void _flushlbf() {
+ // If we flush all streams, we know we've flushed all the line-buffered streams.
+ fflush(NULL);
+}
+
+int __fsetlocking(FILE* fp, int type) {
+ int old_state = _EXT(fp)->_stdio_handles_locking ? FSETLOCKING_INTERNAL : FSETLOCKING_BYCALLER;
+ if (type == FSETLOCKING_QUERY) {
+ return old_state;
+ }
+
+ if (type != FSETLOCKING_INTERNAL && type != FSETLOCKING_BYCALLER) {
+ // The API doesn't let us report an error, so blow up.
+ __libc_fatal("Bad type (%d) passed to __fsetlocking", type);
+ }
+
+ _EXT(fp)->_stdio_handles_locking = (type == FSETLOCKING_INTERNAL);
+ return old_state;
+}
+
+void clearerr_unlocked(FILE* fp) {
+ return __sclearerr(fp);
+}
+
+int feof_unlocked(FILE* fp) {
+ return __sfeof(fp);
+}
+
+int ferror_unlocked(FILE* fp) {
+ return __sferror(fp);
+}