package GrianUtils;
use strict;
use warnings;
use Carp qw/carp croak/;
use Fcntl qw(:flock);
use File::Spec;
use Scalar::Util qw(refaddr reftype);
use List::Util qw(max);
use base 'Exporter';
use Data::Dumper;
use warnings 'all';
BEGIN{
no strict 'refs';
*{caller()."::".$_} = \&Data::Dumper::Dumper for 'Dumper'; #for tests
}
our (@EXPORT, @EXPORT_OK);
our $msg;
@EXPORT_OK=qw(ref_mem_safe my_readdir my_readfile loose $msg total_sv);
*total_sv = \&Storable::AMF::Util::total_sv;
sub loose(&){
my $sub = shift;
my $have = total_sv();
my $delta;
{
my $c;
&$sub() for 1;
};
return $delta unless $delta = $msg = total_sv() - $have;
{
my $c = &$sub();
};
return 0 if total_sv() - $have == $delta;
return $delta unless $delta = $msg = total_sv() - $have;
$have = total_sv();
{
my $c = &$sub();
};
return $delta = $msg = total_sv() - $have;
}
use Carp qw(croak);
#@$a = __PACKAGE__->my_items( 't/AMF0' );
sub my_items{
my $self = shift;
my $directory = shift;
croak "GrianUtils::my_items list context required" unless wantarray;
my @dir_content ;
@dir_content = GrianUtils->my_readdir( ($directory) );
my %items;
my %values;
my %eval;
for (@dir_content){
m/.*[\/\\](.*)\.(.*)/ and $items{ $1}{$2} = $_ or next;
my $val = $values{$1}{$2} = GrianUtils->my_readfile($_);
}
my @item = map $items{$_}, sort keys %items;
# set name property
$_->{ (keys %$_)[0]} =~m/([-\.()\w]+)\./ and $_->{name} ||= $1 for @item;
! $values{$_->{name}} && warn "No name for '".$_->{ (keys %$_)[0]} ."'"for @item;
#read package if ext is pack
for (@item){
if (keys %$_ == 2 && $_->{'pack'}){
my $val = $values{$_->{name}} or next;
%$_ = (%$_ , %{_unpack($val->{'pack'})});
$_->{dump} = $_->{eval} unless defined $_->{dump};
}
else {
my $item = $_;
$_ ne 'name' and $item->{$_} = $values{ $item->{name}}{$_} for keys %$item;
}
}
@item = grep { defined $_->{dump}} @item;
for my $item ( @item){
my $eval = $item->{dump}||=$item->{eval};
no strict;
$item->{obj} = eval $eval;
use strict;
$item->{eval} = $eval;
croak "$item->{name}: $@" if $@;
if ( defined $item->{xml} ){
$item->{eval_xml} = $item->{xml};
$item->{obj_xml} = eval $item->{xml};
croak "$item->{name}: $@" if $@;
}
else {
$item->{eval_xml} = $item->{eval};
$item->{obj_xml} = $item->{obj};
}
}
return @item;
}
sub my_readdir{
my $class = shift;
my $dirname = shift;
my $option = shift || 'abs';
opendir my $SP, $dirname
or die "Can't opendir $dirname for reading";
if ($option eq 'abs') {
return map {File::Spec->catfile($dirname, $_)} grep { $_ !~ m/^\.\.?$/ } readdir $SP;
}
elsif( $option eq 'rel' ) {
return map {$dirname ."/". $_} grep { $_ !~ m/^\./ } readdir $SP;
}
else {
carp "unknown option: $option. Available options are 'abs' or 'rel'";
return ();
}
}
sub my_readfile{
my $class = shift;
my $file = shift;
my @dirs = @_;
my $buf;
$file = File::Spec->catfile(@_, $file);
open my $filefh, "<", $file
or die "Can't open file '$file' for reading";
binmode($filefh);
flock $filefh, LOCK_SH;
read $filefh, $buf, -s $filefh;
flock $filefh, LOCK_UN;
close ($filefh);
return $buf;
}
BEGIN {
our $pack = "(w/a)*";
our @fixed_names = qw(eval amf0 amf3);
sub _pack{
my $hash = shift;
my (@fixed) = delete @$hash{@fixed_names};
#my $s = \ pack "N/aN/aN/a(N/aN/a)*", $eval, $amf0, $amf3, %$hash;
my $s = \ pack $pack, @fixed, %$hash;
@$hash{@fixed_names} = (@fixed);
return $$s;
}
sub _unpack{
my (@fixed, %rest);
(@fixed[0..$#fixed_names], %rest) = unpack $pack, $_[0];
@rest{@fixed_names} = (@fixed);
return \%rest;
};
};
sub create_pack{
my $class = shift;
my $dir = shift;
my $name = shift;
my $value = shift;
$dir=~s/[\/\\]$//;
my $pack_name = File::Spec->catfile($dir, "$name.pack");
my $sname = $pack_name;
$sname =~ s/\.pack$//;
our %folder;
$folder{$sname} = $value;
delete $folder{$sname}{'pack'};
open my $fh, ">", $pack_name or die "can't create $pack_name";
binmode($fh);
print $fh _pack($folder{$sname});
close($fh);
}
sub abs2rel{
my $class = shift;
my $abs_path = shift;
my $base = shift;
$base=~s/[\\\/]$//;
$base=~s/\\/\//g;
$abs_path=~s/\\/\//g;
if ($base eq '.'){
$base=~s/^\.//g;
$abs_path=~s/^\.\///g;
return "./$abs_path";
}
print STDERR "path='$abs_path' base='$base'\n";
carp "Path can't transformed to relative: path='$abs_path' base='$base'" unless substr($abs_path, 0, length($base)) eq $base;
return ".".substr($abs_path, length($base));
}
# not tested yet
sub rel2abs{
my $class = shift;
my $rel_path = shift;
my $base = shift;
$base=~s/[\\\/]$//;
$rel_path=~s/^\.\///;
carp "Path isn't relative: path='$rel_path' base='$base'" if $rel_path=~/^[\\\/]/;
return File::Spec->catfile($base, $rel_path);
}
sub _all_refs_addr{
my $c = shift;
while(@_){
my $item = shift;
next unless refaddr $item;
next if $$c{refaddr $item};
#print refaddr $item, "\n";
$$c{refaddr $item} = 1;
if (reftype $item eq 'ARRAY'){
_all_refs_addr($c, @$item);
}
elsif (reftype $item eq 'HASH') {
_all_refs_addr($c, $_);
}
elsif (reftype $item eq 'SCALAR') {
}
elsif (reftype $item eq 'REF'){
_all_refs_addr($c, $$item)
}
else {
croak "Unsupported type ". reftype $item;
}
}
return keys %$c;
}
sub ref_mem_safe {
my $sub = shift;
my $count_to_execute = shift || 400;
my $count_to_be_ok = shift || 50;
my $nu = -1;
my @addresses;
my %addr;
my $old_max = 0;
for ( my $round = 1 ; $round <= $count_to_execute ; ++$round ) {
my @seq = &$sub();
push @seq, ( \my $b ), [], {}, [], {}, \my $a;
my $new_max = max( _all_refs_addr( {}, @seq, ) );
if ( $old_max < $new_max ) {
$old_max = $new_max;
$nu = -1;
}
else {
++$nu;
}
return $round, $round if ( $nu > $count_to_be_ok );
@seq = ();
}
return ( 0, "$nu/$count_to_be_ok, $count_to_execute" ) if wantarray;
return 0;
}
sub my_create_file{
my $class = shift;
my $file = shift;
my $content = shift;
my $base = shift;
my $usage = 'GrianUtils->my_create_file($file, $content, $base)...';
warn "$usage: \$base not is option" unless $base;
croak "$usage: double dot in \$file restricted" if $file=~m/\.\./;
$base ||= '.';
carp "$usage: \$base --- ($base) is not a directory" unless -d $base;
my @r = split "/", $file;
my $lfile = pop @r;
my $loc_folder = File::Spec->catfile($base, @r);
if (-d -w $loc_folder) {
my $loc_file;
open my $fh, ">", $loc_file = File::Spec->catfile($base, $file)
or croak "$usage: Can't create file($loc_file)";
binmode($fh);
print $fh $content;
close($fh);
}
elsif (-d _) {
croak "$usage: Not writeable directory($loc_folder)";
}
else {
# Generate path for
my @folders;
my $folder = $base;
for my $r (@r){
$folder = File::Spec->catfile($folder, $r);
next if (-d $folder);
mkdir($folder)
or croak "$usage: Can't create directory ($folder) for path($loc_folder)";
}
$class->my_create_file($file, $content, $base);
}
}
1;