diff options
Diffstat (limited to 'usr/src/lib/libast/common/sfio/sfsetbuf.c')
-rw-r--r-- | usr/src/lib/libast/common/sfio/sfsetbuf.c | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/usr/src/lib/libast/common/sfio/sfsetbuf.c b/usr/src/lib/libast/common/sfio/sfsetbuf.c new file mode 100644 index 0000000000..79748507fe --- /dev/null +++ b/usr/src/lib/libast/common/sfio/sfsetbuf.c @@ -0,0 +1,403 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#if defined(__STDPP__directive) && defined(__STDPP__hide) +__STDPP__directive pragma pp:hide getpagesize +#else +#define getpagesize ______getpagesize +#endif + +#include "sfhdr.h" + +#if defined(__STDPP__directive) && defined(__STDPP__hide) +__STDPP__directive pragma pp:nohide getpagesize +#else +#undef getpagesize +#endif + +#if _lib_getpagesize +_BEGIN_EXTERNS_ +extern int getpagesize _ARG_((void)); +_END_EXTERNS_ +#endif + +/* Set a (new) buffer for a stream. +** If size < 0, it is assigned a suitable value depending on the +** kind of stream. The actual buffer size allocated is dependent +** on how much memory is available. +** +** Written by Kiem-Phong Vo. +*/ + +#if !_sys_stat +struct stat +{ int st_mode; + int st_size; +}; +#undef sysfstatf +#define sysfstatf(fd,st) (-1) +#endif /*_sys_stat*/ + +static int setlinemode() +{ char* astsfio; + char* endw; + + static int modes = -1; + static const char sf_line[] = "SF_LINE"; + static const char sf_wcwidth[] = "SF_WCWIDTH"; + +#define ISSEPAR(c) ((c) == ',' || (c) == ' ' || (c) == '\t') + if (modes < 0) + { modes = 0; + if(astsfio = getenv("_AST_SFIO_OPTIONS")) + { for(; *astsfio != 0; astsfio = endw) + { while(ISSEPAR(*astsfio) ) + *astsfio++; + for(endw = astsfio; *endw && !ISSEPAR(*endw); ++endw) + ; + if((endw-astsfio) == (sizeof(sf_line)-1) && + strncmp(astsfio,sf_line,endw-astsfio) == 0) + { if ((modes |= SF_LINE) == (SF_LINE|SF_WCWIDTH)) + break; + } + else if((endw-astsfio) == (sizeof(sf_wcwidth)-1) && + strncmp(astsfio,sf_wcwidth,endw-astsfio) == 0) + { if ((modes |= SF_WCWIDTH) == (SF_LINE|SF_WCWIDTH)) + break; + } + } + } + } + return modes; +} + +#if __STD_C +Void_t* sfsetbuf(reg Sfio_t* f, reg Void_t* buf, reg size_t size) +#else +Void_t* sfsetbuf(f,buf,size) +reg Sfio_t* f; /* stream to be buffered */ +reg Void_t* buf; /* new buffer */ +reg size_t size; /* buffer size, -1 for default size */ +#endif +{ + int sf_malloc, oflags, init, okmmap, local; + ssize_t bufsize, blksz; + Sfdisc_t* disc; + sfstat_t st; + uchar* obuf = NIL(uchar*); + ssize_t osize = 0; + + SFONCE(); + + SFMTXSTART(f,NIL(Void_t*)); + + GETLOCAL(f,local); + + if(size == 0 && buf) + { /* special case to get buffer info */ + _Sfi = f->val = (f->bits&SF_MMAP) ? (f->endb-f->data) : f->size; + SFMTXRETURN(f, (Void_t*)f->data); + } + + /* cleanup actions already done, don't allow write buffering any more */ + if(_Sfexiting && !(f->flags&SF_STRING) && (f->mode&SF_WRITE)) + { buf = NIL(Void_t*); + size = 0; + } + + if((init = f->mode&SF_INIT) ) + { if(!f->pool && _sfsetpool(f) < 0) + SFMTXRETURN(f, NIL(Void_t*)); + } + else if((f->mode&SF_RDWR) != SFMODE(f,local) && _sfmode(f,0,local) < 0) + SFMTXRETURN(f, NIL(Void_t*)); + + if(init) + f->mode = (f->mode&SF_RDWR)|SF_LOCK; + else + { int rv; + + /* make sure there is no hidden read data */ + if(f->proc && (f->flags&SF_READ) && (f->mode&SF_WRITE) && + _sfmode(f,SF_READ,local) < 0) + SFMTXRETURN(f, NIL(Void_t*)); + + /* synchronize first */ + SFLOCK(f,local); rv = SFSYNC(f); SFOPEN(f,local); + if(rv < 0) + SFMTXRETURN(f, NIL(Void_t*)); + + /* turn off the SF_SYNCED bit because buffer is changing */ + f->mode &= ~SF_SYNCED; + } + + SFLOCK(f,local); + + if((Sfio_t*)buf != f) + blksz = -1; + else /* setting alignment size only */ + { blksz = (ssize_t)size; + + if(!init) /* stream already initialized */ + { obuf = f->data; + osize = f->size; + goto done; + } + else /* initialize stream as if in the default case */ + { buf = NIL(Void_t*); + size = (size_t)SF_UNBOUND; + } + } + + bufsize = 0; + oflags = f->flags; + + /* see if memory mapping is possible (see sfwrite for SF_BOTH) */ + okmmap = (buf || (f->flags&SF_STRING) || (f->flags&SF_RDWR) == SF_RDWR) ? 0 : 1; + + /* save old buffer info */ +#ifdef MAP_TYPE + if(f->bits&SF_MMAP) + { if(f->data) + { SFMUNMAP(f,f->data,f->endb-f->data); + f->data = NIL(uchar*); + } + } else +#endif + if(f->data == f->tiny) + { f->data = NIL(uchar*); + f->size = 0; + } + obuf = f->data; + osize = f->size; + + f->flags &= ~SF_MALLOC; + f->bits &= ~SF_MMAP; + + /* pure read/string streams must have a valid string */ + if((f->flags&(SF_RDWR|SF_STRING)) == SF_RDSTR && + (size == (size_t)SF_UNBOUND || !buf)) + size = 0; + + /* set disc to the first discipline with a seekf */ + for(disc = f->disc; disc; disc = disc->disc) + if(disc->seekf) + break; + + if((init || local) && !(f->flags&SF_STRING)) + { /* ASSERT(f->file >= 0) */ + st.st_mode = 0; + + /* if has discipline, set size by discipline if possible */ + if(!_sys_stat || disc) + { if((f->here = SFSK(f,(Sfoff_t)0,SEEK_CUR,disc)) < 0) + goto unseekable; + else + { Sfoff_t e; + if((e = SFSK(f,(Sfoff_t)0,SEEK_END,disc)) >= 0) + f->extent = e > f->here ? e : f->here; + (void)SFSK(f,f->here,SEEK_SET,disc); + goto setbuf; + } + } + + /* get file descriptor status */ + if(sysfstatf((int)f->file,&st) < 0) + f->here = -1; + else + { +#if _sys_stat && _stat_blksize /* preferred io block size */ + f->blksz = (size_t)st.st_blksize; +#endif + bufsize = 64 * 1024; + if(S_ISDIR(st.st_mode) || (Sfoff_t)st.st_size < (Sfoff_t)SF_GRAIN) + okmmap = 0; + if(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) + f->here = SFSK(f,(Sfoff_t)0,SEEK_CUR,f->disc); + else f->here = -1; + +#if O_TEXT /* no memory mapping with O_TEXT because read()/write() alter data stream */ + if(okmmap && f->here >= 0 && + (sysfcntlf((int)f->file,F_GETFL,0) & O_TEXT) ) + okmmap = 0; +#endif + } + + if(init) + f->flags |= setlinemode(); + + if(f->here >= 0) + { f->extent = (Sfoff_t)st.st_size; + + /* seekable std-devices are share-public by default */ + if(f == sfstdin || f == sfstdout || f == sfstderr) + f->flags |= SF_SHARE|SF_PUBLIC; + } + else + { + unseekable: + f->extent = -1; + f->here = 0; + + if(init) + { if(S_ISCHR(st.st_mode) ) + { int oerrno = errno; + + bufsize = SF_GRAIN; + + /* set line mode for terminals */ + if(!(f->flags&(SF_LINE|SF_WCWIDTH)) && isatty(f->file)) + f->flags |= SF_LINE|SF_WCWIDTH; +#if _sys_stat + else /* special case /dev/null */ + { reg int dev, ino; + dev = (int)st.st_dev; + ino = (int)st.st_ino; + if(sysstatf(DEVNULL,&st) >= 0 && + dev == (int)st.st_dev && + ino == (int)st.st_ino) + SFSETNULL(f); + } +#endif + errno = oerrno; + } + + /* initialize side buffer for r+w unseekable streams */ + if(!f->proc && (f->bits&SF_BOTH) ) + (void)_sfpopen(f,-1,-1,1); + } + } + + /* set page size, this is also the desired default buffer size */ + if(_Sfpage <= 0) + { +#if _lib_getpagesize + if((_Sfpage = (size_t)getpagesize()) <= 0) +#endif + _Sfpage = SF_PAGE; + } + } + +#ifdef MAP_TYPE + if(okmmap && size && (f->mode&SF_READ) && f->extent >= 0 ) + { /* see if we can try memory mapping */ + if(!disc) + for(disc = f->disc; disc; disc = disc->disc) + if(disc->readf) + break; + if(!disc) + { f->bits |= SF_MMAP; + if(size == (size_t)SF_UNBOUND) + { if(bufsize > _Sfpage) + size = bufsize * SF_NMAP; + else size = _Sfpage * SF_NMAP; + if(size > 256*1024) + size = 256*1024; + } + } + } +#endif + + /* get buffer space */ +setbuf: + if(size == (size_t)SF_UNBOUND) + { /* define a default size suitable for block transfer */ + if(init && osize > 0) + size = osize; + else if(f == sfstderr && (f->mode&SF_WRITE)) + size = 0; + else if(f->flags&SF_STRING ) + size = SF_GRAIN; + else if((f->flags&SF_READ) && !(f->bits&SF_BOTH) && + f->extent > 0 && f->extent < (Sfoff_t)_Sfpage ) + size = (((size_t)f->extent + SF_GRAIN-1)/SF_GRAIN)*SF_GRAIN; + else if((ssize_t)(size = _Sfpage) < bufsize) + size = bufsize; + + buf = NIL(Void_t*); + } + + sf_malloc = 0; + if(size > 0 && !buf && !(f->bits&SF_MMAP)) + { /* try to allocate a buffer */ + if(obuf && size == (size_t)osize && init) + { buf = (Void_t*)obuf; + obuf = NIL(uchar*); + sf_malloc = (oflags&SF_MALLOC); + } + if(!buf) + { /* do allocation */ + while(!buf && size > 0) + { if((buf = (Void_t*)malloc(size)) ) + break; + else size /= 2; + } + if(size > 0) + sf_malloc = SF_MALLOC; + } + } + + if(size == 0 && !(f->flags&SF_STRING) && !(f->bits&SF_MMAP) && (f->mode&SF_READ)) + { /* use the internal buffer */ + size = sizeof(f->tiny); + buf = (Void_t*)f->tiny; + } + + /* set up new buffer */ + f->size = size; + f->next = f->data = f->endr = f->endw = (uchar*)buf; + f->endb = (f->mode&SF_READ) ? f->data : f->data+size; + if(f->flags&SF_STRING) + { /* these fields are used to test actual size - see sfseek() */ + f->extent = (!sf_malloc && + ((f->flags&SF_READ) || (f->bits&SF_BOTH)) ) ? size : 0; + f->here = 0; + + /* read+string stream should have all data available */ + if((f->mode&SF_READ) && !sf_malloc) + f->endb = f->data+size; + } + + f->flags = (f->flags & ~SF_MALLOC)|sf_malloc; + + if(obuf && obuf != f->data && osize > 0 && (oflags&SF_MALLOC)) + { free((Void_t*)obuf); + obuf = NIL(uchar*); + } + +done: + _Sfi = f->val = obuf ? osize : 0; + + /* blksz is used for aligning disk block boundary while reading data to + ** optimize data transfer from disk (eg, via direct I/O). blksz can be + ** at most f->size/2 so that data movement in buffer can be optimized. + ** blksz should also be a power-of-2 for optimal disk seeks. + */ + if(blksz <= 0 || (blksz & (blksz-1)) != 0 ) + blksz = SF_GRAIN; + while(blksz > f->size/2) + blksz /= 2; + f->blksz = blksz; + + SFOPEN(f,local); + + SFMTXRETURN(f, (Void_t*)obuf); +} |