#include "sfhdr.h"
typedef struct _dccache_s
{ Sfdisc_t disc;
uchar* data;
uchar* endb;
} Dccache_t;
#if __STD_C
static int _dccaexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
#else
static int _dccaexcept(f,type,val,disc)
Sfio_t* f;
int type;
Void_t* val;
Sfdisc_t* disc;
#endif
{
if(disc && type == SF_FINAL)
free(disc);
return 0;
}
#if __STD_C
static ssize_t _dccaread(Sfio_t* f, Void_t* buf, size_t size, Sfdisc_t* disc)
#else
static ssize_t _dccaread(f, buf, size, disc)
Sfio_t* f;
Void_t* buf;
size_t size;
Sfdisc_t* disc;
#endif
{
ssize_t sz;
Sfdisc_t *prev;
Dccache_t *dcca;
if(!f)
return -1;
for(prev = f->disc; prev; prev = prev->disc)
if(prev->disc == disc)
break;
if(!prev)
return -1;
if(size <= 0)
return size;
dcca = (Dccache_t*)disc;
if((sz = dcca->endb - dcca->data) > (ssize_t)size)
sz = (ssize_t)size;
memcpy(buf, dcca->data, sz);
if((dcca->data += sz) >= dcca->endb)
{ prev->disc = disc->disc;
free(disc);
}
return sz;
}
#if __STD_C
Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc)
#else
Sfdisc_t* sfdisc(f,disc)
Sfio_t* f;
Sfdisc_t* disc;
#endif
{
Sfdisc_t *d, *rdisc;
Sfread_f oreadf;
Sfwrite_f owritef;
Sfseek_f oseekf;
ssize_t n;
Dccache_t *dcca = NIL(Dccache_t*);
SFMTXDECL(f);
SFMTXENTER(f, NIL(Sfdisc_t*));
if((Sfio_t*)disc == f)
SFMTXRETURN(f,f->disc);
if((f->flags&SF_READ) && f->proc && (f->mode&SF_WRITE) )
{
if(_sfmode(f,SF_READ,0) < 0)
SFMTXRETURN(f, NIL(Sfdisc_t*));
}
else
{ if((f->mode&SF_RDWR) != f->mode && _sfmode(f,0,0) < 0)
SFMTXRETURN(f, NIL(Sfdisc_t*));
}
SFLOCK(f,0);
rdisc = NIL(Sfdisc_t*);
if(!disc && f->disc && f->disc->disc && f->disc->disc->readf == _dccaread )
goto done;
if(!(f->flags&SF_STRING))
{ (void)SFSYNC(f);
if((f->mode&SF_READ) && (f->mode&SF_SYNCED) )
{ f->mode &= ~SF_SYNCED;
f->endb = f->next = f->endr = f->endw = f->data;
}
if(((f->mode&SF_WRITE) && (n = f->next-f->data) > 0) ||
((f->mode&SF_READ) && (n = f->endb-f->next) > 0) )
{ int rv = 0;
if(rv == 0 && f->disc && f->disc->exceptf)
{ SFOPEN(f,0);
rv = (*f->disc->exceptf)(f, SF_DBUFFER, &n, f->disc);
SFLOCK(f,0);
}
if(rv == 0 && disc && disc->exceptf)
{ SFOPEN(f,0);
rv = (*disc->exceptf)(f, SF_DBUFFER, &n, disc);
SFLOCK(f,0);
}
if(rv < 0)
goto done;
}
if((f->mode&SF_READ) && n > 0 && disc && disc->readf )
{ if(!(dcca = (Dccache_t*)malloc(sizeof(Dccache_t)+n)) )
goto done;
memclear(dcca, sizeof(Dccache_t));
dcca->disc.readf = _dccaread;
dcca->disc.exceptf = _dccaexcept;
dcca->data = ((uchar*)dcca) + sizeof(Dccache_t);
dcca->endb = dcca->data + n;
memcpy(dcca->data, f->next, n);
f->endb = f->next = f->endr = f->endw = f->data;
}
}
#define GETDISCF(func,iof,type) \
{ for(d = f->disc; d && !d->iof; d = d->disc) ; \
func = d ? d->iof : NIL(type); \
}
GETDISCF(oreadf,readf,Sfread_f);
GETDISCF(owritef,writef,Sfwrite_f);
GETDISCF(oseekf,seekf,Sfseek_f);
if(disc == SF_POPDISC)
{
if(!(d = f->disc) )
goto done;
disc = d->disc;
if(d->exceptf)
{ SFOPEN(f,0);
if((*(d->exceptf))(f,SF_DPOP,(Void_t*)disc,d) < 0 )
goto done;
SFLOCK(f,0);
}
f->disc = disc;
rdisc = d;
}
else
{
do
{
d = f->disc;
if(d && d->exceptf)
{ SFOPEN(f,0);
if( (*(d->exceptf))(f,SF_DPUSH,(Void_t*)disc,d) < 0 )
goto done;
SFLOCK(f,0);
}
} while(d != f->disc);
for(; d; d = d->disc)
if(d == disc)
goto done;
if(dcca)
{ dcca->disc.disc = f->disc;
disc->disc = &dcca->disc;
}
else disc->disc = f->disc;
f->disc = disc;
rdisc = disc;
}
if(!(f->flags&SF_STRING) )
{
reg int reinit = 0;
#define DISCF(dst,iof,type) (dst ? dst->iof : NIL(type))
#define REINIT(oiof,iof,type) \
if(!reinit) \
{ for(d = f->disc; d && !d->iof; d = d->disc) ; \
if(DISCF(d,iof,type) != oiof) \
reinit = 1; \
}
REINIT(oreadf,readf,Sfread_f);
REINIT(owritef,writef,Sfwrite_f);
REINIT(oseekf,seekf,Sfseek_f);
if(reinit)
{ SETLOCAL(f);
f->bits &= ~SF_NULL;
if((f->bits&SF_MMAP) || (f->mode&SF_INIT))
sfsetbuf(f,NIL(Void_t*),(size_t)SF_UNBOUND);
else if(f->data == f->tiny)
sfsetbuf(f,NIL(Void_t*),0);
else
{ int flags = f->flags;
sfsetbuf(f,(Void_t*)f->data,f->size);
f->flags |= (flags&SF_MALLOC);
}
}
}
done :
SFOPEN(f,0);
SFMTXRETURN(f, rdisc);
}