123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
/*******************************************************************************

        copyright:      Copyright (c) 2007 Kris Bell. All rights reserved

        license:        BSD style: $(LICENSE)

        version:        Initial release: Oct 2007

        author:         Kris

*******************************************************************************/

module tango.io.stream.Snoop;

private import  tango.io.Console,
                tango.io.device.Conduit;

private import  tango.text.convert.Format;

version(DigitalMars)
{
    private import tango.core.Vararg;
}else version (GNU)
    private import tango.core.Vararg;

private alias void delegate(const(char)[]) Snoop;

/*******************************************************************************

        Stream to expose call behaviour. By default, activity trace is
        sent to Cerr.

*******************************************************************************/

class SnoopInput : InputStream
{
        private InputStream     host;
        private Snoop           snoop;

        /***********************************************************************

                Attach to the provided stream.

        ***********************************************************************/

        this (InputStream host, Snoop snoop = null)
        {
                assert (host);
                this.host = host;
                this.snoop = snoop ? snoop : &snooper;
        }

        /***********************************************************************

                Return the upstream host of this filter.

        ***********************************************************************/

        InputStream input ()
        {
                return host;
        }

        /***********************************************************************

                Return the hosting conduit.

        ***********************************************************************/

        final IConduit conduit ()
        {
                return host.conduit;
        }

        /***********************************************************************

                Read from conduit into a target array. The provided dst
                will be populated with content from the conduit.

                Returns the number of bytes read, which may be less than
                requested in dst.

        ***********************************************************************/

        final size_t read (void[] dst)
        {
                auto x = host.read (dst);
                trace ("{}: read {} bytes", host.conduit, x is -1 ? 0 : x);
                return x;
        }

        /***********************************************************************

                Load the bits from a stream, and return them all in an
                array. The dst array can be provided as an option, which
                will be expanded as necessary to consume the input.

                Returns an array representing the content, and throws
                IOException on error.

        ***********************************************************************/

        void[] load (size_t max=-1)
        {
                auto x = host.load (max);
                trace ("{}: loaded {} bytes", x.length);
                return x;
        }

        /***********************************************************************

                Clear any buffered content.

        ***********************************************************************/

        final InputStream flush ()
        {
                host.flush();
                trace ("{}: flushed/cleared", host.conduit);
                return this;
        }

        /***********************************************************************

                Close the input.

        ***********************************************************************/

        final void close ()
        {
                host.close();
                trace ("{}: closed", host.conduit);
        }

        /***********************************************************************

                Seek on this stream. Target conduits that don't support
                seeking will throw an IOException.

        ***********************************************************************/

        final long seek (long offset, Anchor anchor = Anchor.Begin)
        {
                auto s = host.seek (offset, anchor);
                trace ("{}: seek at offset {} from anchor {}", host.conduit, offset, anchor);
                return s;
        }

        /***********************************************************************

                Internal trace handler.

        ***********************************************************************/

        private void snooper (const(char)[] x)
        {
                Cerr(x).newline;
        }

        /***********************************************************************

                Internal trace handler.

        ***********************************************************************/

        private void trace (const(char)[] format, ...)
        {
                char[256] tmp = void;
                snoop (Format.vprint (tmp, format, _arguments, _argptr));
        }
}


/*******************************************************************************

        Stream to expose call behaviour. By default, activity trace is
        sent to Cerr.

*******************************************************************************/

class SnoopOutput : OutputStream
{
        private OutputStream    host;
        private Snoop           snoop;

        /***********************************************************************

                Attach to the provided stream.

        ***********************************************************************/

        this (OutputStream host, Snoop snoop = null)
        {
                assert (host);
                this.host = host;
                this.snoop = snoop ? snoop : &snooper;
        }

        /***********************************************************************

                Return the upstream host of this filter.

        ***********************************************************************/

        OutputStream output ()
        {
                return host;
        }

        /***********************************************************************

                Write to conduit from a source array. The provided src
                content will be written to the conduit.

                Returns the number of bytes written from src, which may
                be less than the quantity provided.

        ***********************************************************************/

        final size_t write (const(void)[] src)
        {
                auto x = host.write (src);
                trace ("{}: wrote {} bytes", host.conduit, x is -1 ? 0 : x);
                return x;
        }

        /***********************************************************************

                Return the hosting conduit.

        ***********************************************************************/

        final IConduit conduit ()
        {
                return host.conduit;
        }

        /***********************************************************************

                Emit/purge buffered content.

        ***********************************************************************/

        final OutputStream flush ()
        {
                host.flush();
                trace ("{}: flushed", host.conduit);
                return this;
        }

        /***********************************************************************

                Close the output.

        ***********************************************************************/

        final void close ()
        {
                host.close();
                trace ("{}: closed", host.conduit);
        }

        /***********************************************************************

                Transfer the content of another conduit to this one. Returns
                a reference to this class, or throws IOException on failure.

        ***********************************************************************/

        final OutputStream copy (InputStream src, size_t max=-1)
        {
                host.copy (src, max);
                trace("{}: copied from {}", host.conduit, src.conduit);
                return this;
        }

        /***********************************************************************

                Seek on this stream. Target conduits that don't support
                seeking will throw an IOException.

        ***********************************************************************/

        final long seek (long offset, Anchor anchor = Anchor.Begin)
        {
                auto s = host.seek (offset, anchor);
                trace ("{}: seek at offset {} from anchor {}", host.conduit, offset, anchor);
                return s;
        }

        /***********************************************************************

                Internal trace handler.

        ***********************************************************************/

        private void snooper (const(char)[] x)
        {
                Cerr(x).newline;
        }

        /***********************************************************************

                Internal trace handler.

        ***********************************************************************/

        private void trace (const(char)[] format, ...)
        {
                char[256] tmp = void;
                snoop (Format.vprint (tmp, format, _arguments, _argptr));
        }
}



debug (Snoop)
{
        void main()
        {
                auto s = new SnoopInput (null);
                auto o = new SnoopOutput (null);
        }
}