# ==================================================================
# Gossamer Threads Module Library - http://gossamer-threads.com/
#
#   GT::IPC::Filter::Block
#   Author  : Scott Beck
#   CVS Info : 087,071,086,086,085      
#   $Id: Block.pm,v 1.7 2004/01/13 01:35:17 jagerman Exp $
#
# Copyright (c) 2004 Gossamer Threads Inc.  All Rights Reserved.
# ==================================================================
#
# Description: Filter streams of input out in block sizes.
#

package GT::IPC::Filter::Block;
# ==================================================================

use strict;
use base 'GT::Base';

sub new {
# ----------------------------------------------------------------------------
    my $class = shift;

    if (@_ == 1) {
        @_ = (output => $_[0]);
    }
    $class->fatal(BADARGS => "Arguments to new() must be a hash")
        if @_ & 1;
    my %opts = @_;

    my $output = delete $opts{output};
    $class->fatal(BADARGS => "No output for new()")
        unless defined $output;
    $class->fatal(BADARGS => "No output passed to new() is not a code ref")
        unless ref($output) eq 'CODE';

    my $block_size = delete $opts{block_size};
    $block_size = 512 unless defined $block_size;

    return bless {
        block_size => $block_size,
        output     => $output,
    }, $class;
}

sub put {
# ----------------------------------------------------------------------------
    my ($self, $in) = @_;

    if (defined $self->{buffer}) {
        $$in = $self->{buffer} . $$in;
        undef $self->{buffer};
    }
    if (length($$in) >= $self->{block_size}) {
        my $gets = int(length($$in) / $self->{block_size});
        for (1 .. $gets) {
            $self->{output}->(substr($$in, 0, $self->{block_size}));
            substr($$in, 0, $self->{block_size}) = '';
        }
    }
    $self->{buffer} = $$in;
}

sub flush {
# ----------------------------------------------------------------------------
    my ($self) = @_;
    $self->{output}->($self->{buffer}) if defined $self->{buffer};
    undef $self->{buffer};
}

1;

__END__

=head1 NAME

GT::IPC::Filter::Block - Implements block based filtering for output streams.

=head1 SYNOPSIS

    use GT::IPC::Filter::Block;

    my $filter = new GT::IPC::Filter::Block(
        sub { my $block = shift ... }
    );
    # -or-
    my $filter = new GT::IPC::Filter::Block(
        output => sub { my $out = shift; .. },
        block_size  => 512 # Default
    );

    $filter->put(\$data);

    $filter->flush;

=head1 DESCRIPTION

Implements block based filtering to an output code reference. Used mainly in
GT::IPC::Run, L<GT::IPC::Run> for details.

=head1 METHODS

There are three methods (as with all filters in this class).

=head2 new

Takes either a single argument, which is a code reference to call output with,
or a hash of options.

=over 4

=item output

This is the code reference you would like called with each block of output.
The blocks are stripped of there ending before this is called.

=item block_size

This is the size of chunks of data you want your code reference called with. It
defaults to 512.

=back

=head2 put

This method takes a stream of data, it converted it into block based data using
the C<block_size> you specified and passes each block to the code reference
specified by C<new()>, see L<"new">.  There is buffering that happens here.

=head2 flush

This method should be called last, when the data stream is over. It flushes the
remaining buffer out to the code reference.

=head1 SEE ALSO

See L<GT::IPC::Run>.

=head1 MAINTAINER

Scott Beck

=head1 COPYRIGHT

Copyright (c) 2004 Gossamer Threads Inc.  All Rights Reserved.
http://www.gossamer-threads.com/

=head1 VERSION

Revision: $Id: Block.pm,v 1.7 2004/01/13 01:35:17 jagerman Exp $

=cut

