diff options
Diffstat (limited to 'libc/stdio')
-rw-r--r-- | libc/stdio/fileext.h | 20 | ||||
-rw-r--r-- | libc/stdio/findfp.c | 8 | ||||
-rw-r--r-- | libc/stdio/fread.c | 105 | ||||
-rw-r--r-- | libc/stdio/local.h | 6 | ||||
-rw-r--r-- | libc/stdio/stdio.c | 90 | ||||
-rw-r--r-- | libc/stdio/stdio_ext.cpp | 101 |
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); +} |