summaryrefslogtreecommitdiff
path: root/usr/src/lib/libast/common/sfio/sfsetbuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libast/common/sfio/sfsetbuf.c')
-rw-r--r--usr/src/lib/libast/common/sfio/sfsetbuf.c403
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);
+}