sfsetbuf.c revision 34f9b3eef6fdadbda0a846aa4d68691ac40eace5
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1985-2009 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* 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(Sfio_t* f, Void_t* buf, size_t size)
#else
Void_t* sfsetbuf(f,buf,size)
Sfio_t* f; /* stream to be buffered */
Void_t* buf; /* new buffer */
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;
SFMTXDECL(f);
SFONCE();
SFMTXENTER(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);
}