# Copyright 1999-2016. Parallels IP Holdings GmbH. All Rights Reserved. package Storage::Splitter; # # This 'filter' splits the incoming data to the files and # reports back the name and size of each through the # control file descriptor. # # output data is in format 'partName partSize' # use strict; use POSIX; use IO::Handle; use IO::File; my $blocksize = 65536; my $splitter; my $generateUniqueName; sub init_process { $splitter = Storage::Splitter->new(@_); } sub run { eval { my $block; binmode STDIN; while (my $blocklen = sysread(STDIN, $block, $blocksize)) { $splitter->addData($block, $blocklen); } $splitter->finish(); }; if ($@) { if ($splitter->{part_fh} && $splitter->{part_name}) { unlink($splitter->{part_name}); } print STDERR $@; POSIX::_exit(1); } POSIX::_exit(0); } # -- instance methods -- sub new { my $self = {}; bless($self, shift); $self->init(@_); return $self; } sub init { my ($self, $controlFd, $partsSize, $partsName, $dstDir, $dstExt ) = @_; $self->{control_fh} = IO::Handle->new_from_fd($controlFd, "w") or die "Unable to open 'control' filehandle in spritter"; $self->{parts_size} = $partsSize; $self->{dstdir} = $dstDir; $self->{ext} = $dstExt; $self->{parts_name} = $partsName; $self->{part_size} = 0; $self->{curr_part_num} = -1; } sub _writeToPart { my ($self, $chunk, $size) = @_; return if !$size; if (!$self->{part_fh}) { $self->{curr_part_num}++; if ($self->{parts_name} eq "-") { $self->{part_fh} = \*STDOUT; } else { $self->{part_name} = Storage::Splitter::generateUniqueFileName( $self->{dstdir} . '/' . $self->{parts_name}, $self->{ext}, $self->{curr_part_num} ); $self->{part_fh} = IO::File->new($self->{part_name}, "w"); die "Unable to create the output file '$self->{part_name}': $!" if not $self->{part_fh}; } binmode $self->{part_fh}; } $self->{part_size} += $size; my $offset = 0; while ($size) { my $written = $self->{part_fh}->syswrite($chunk, $size, $offset); die "Unable to write to the output file '$self->{part_name}': $!" unless defined $written; $offset += $written; $size -= $written; } } sub _finishPart { my ($self) = @_; if ($self->{part_fh}) { $self->{part_fh}->close(); undef $self->{part_fh}; $self->{control_fh}->printf("%s %.0f\n", substr( $self->{part_name}, length($self->{dstdir}) + 1 ), $self->{part_size}); $self->{control_fh}->flush(); $self->{part_size} = 0; } } sub addData { my ($self, $chunk, $chunkLen) = @_; while ($self->{parts_size} and $self->{part_size} + $chunkLen >= $self->{parts_size}) { # $chunk -> ($end_part_chunk, $chunk) my $end_part_chunk = substr($chunk, 0, $self->{parts_size} - $self->{part_size}, ''); $chunkLen = $self->{part_size} + $chunkLen - $self->{parts_size}; $self->_writeToPart($end_part_chunk, $self->{parts_size} - $self->{part_size}); $self->_finishPart(); } $self->_writeToPart($chunk, $chunkLen); } sub finish { my ($self) = @_; $self->_finishPart(); $self->{control_fh}->close(); } sub generateUniqueFileName{ my ($filename, $ext, $counter ) = @_; my $fname = "$filename$ext"; $fname = "$fname$counter" if $counter>0; if( $generateUniqueName and -e $fname ){ do{ $counter += 1; $fname = "$filename$ext$counter"; }while( -e $fname ); } return $fname; } 1; # # Test # # Usage: perl Splitter.pm < file # Expected output: file split to the bunch of pieces 'foobar.NNN' of 11 bytes each. # output - info of each piece as described in the beginning on the file # # # init_process(1, 11, "foobar"); # run(); # # Local Variables: # mode: cperl # cperl-indent-level: 2 # indent-tabs-mode: nil # tab-width: 4 # End: