#define PERL_NO_GET_CONTEXT 1
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "perliol.h"
#ifndef PERL_UNUSED_ARG
# define PERL_UNUSED_ARG(x) PERL_UNUSED_VAR(x)
#endif /* !PERL_UNUSED_ARG */
#ifndef Newx
# define Newx(v,n,t) New(0,v,n,t)
#endif /* !Newx */
struct
PerlIOrewindable {
struct
_PerlIO base;
Size_t bufsize;
Size_t filled;
Size_t position;
U8 *buffer;
};
static
IV PerlIOrewindable_pushed(pTHX_ PerlIO *f,
char
const
*mode, SV *arg,
PerlIO_funcs *funcs)
{
struct
PerlIOrewindable *rw = PerlIOSelf(f,
struct
PerlIOrewindable);
PERL_UNUSED_ARG(arg);
{
IV result = PerlIOBase_pushed(aTHX_ f, mode, NULL, funcs);
if
(result != 0)
return
result;
}
rw->bufsize = 1;
rw->filled = 0;
rw->position = 0;
Newx(rw->buffer, 1, U8);
return
0;
}
static
IV PerlIOrewindable_popped(pTHX_ PerlIO *f)
{
struct
PerlIOrewindable *rw = PerlIOSelf(f,
struct
PerlIOrewindable);
if
(rw->position != rw->filled) {
PerlIOBase_unread(aTHX_ PerlIONext(f),
rw->buffer + rw->position, rw->filled - rw->position);
}
Safefree(rw->buffer);
return
0;
}
static
SSize_t PerlIOrewindable_read(pTHX_ PerlIO *f,
void
*vbuf, Size_t count)
{
struct
PerlIOrewindable *rw = PerlIOSelf(f,
struct
PerlIOrewindable);
U8 *cbuf = vbuf;
Size_t pos = rw->position;
SSize_t done = 0;
if
(pos != rw->filled) {
Size_t avail = rw->filled - pos;
if
(avail > count) avail = count;
Copy(rw->buffer + pos, cbuf, avail, U8);
pos += avail;
cbuf += avail;
count -= avail;
done = avail;
}
if
(count) {
SSize_t avail = PerlIO_read(PerlIONext(f), cbuf, count);
Size_t endpos;
if
(avail < 0)
return
avail;
endpos = pos + avail;
if
(endpos > rw->bufsize) {
Size_t bufsize = rw->bufsize;
do
{
bufsize <<= 1;
}
while
(endpos > bufsize);
Renew(rw->buffer, bufsize, U8);
rw->bufsize = bufsize;
}
Copy(cbuf, rw->buffer + pos, avail, U8);
rw->filled = pos = endpos;
done += avail;
}
rw->position = pos;
return
done;
}
static
IV PerlIOrewindable_seek(pTHX_ PerlIO *f, Off_t off,
int
whence)
{
struct
PerlIOrewindable *rw = PerlIOSelf(f,
struct
PerlIOrewindable);
switch
(whence) {
case
1: {
off += rw->position;
}
case
0: {
if
(off < 0 || off > (Off_t)rw->filled) {
errno
= EINVAL;
return
-1;
}
rw->position = (Size_t)off;
return
0;
}
break
;
default
: {
errno
= EINVAL;
return
-1;
}
break
;
}
}
static
Off_t PerlIOrewindable_tell(pTHX_ PerlIO *f)
{
struct
PerlIOrewindable *rw = PerlIOSelf(f,
struct
PerlIOrewindable);
return
rw->position;
}
static
PerlIO_funcs PerlIOrewindable_funcs = {
sizeof
(PerlIO_funcs),
"rewindable"
,
sizeof
(
struct
PerlIOrewindable),
0,
PerlIOrewindable_pushed,
PerlIOrewindable_popped,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
PerlIOrewindable_read,
NULL
,
NULL
,
PerlIOrewindable_seek,
PerlIOrewindable_tell,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
};
MODULE = PerlIO::rewindable PACKAGE = PerlIO::rewindable
PROTOTYPES: DISABLE
BOOT:
PerlIO_define_layer(aTHX_ &PerlIOrewindable_funcs);