X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=samples%2Fbenchmark%2Fbench_conf_writers.pl;fp=samples%2Fbenchmark%2Fbench_conf_writers.pl;h=37b10703b233c2abe9174e542bba54cce471010d;hb=4eee158dce82376f2f37de29d91c53f60a24aebe;hp=0000000000000000000000000000000000000000;hpb=85070b46d0a93ddbeef07341421adb8389a55418;p=chaz%2Fp5-CGI-Ex diff --git a/samples/benchmark/bench_conf_writers.pl b/samples/benchmark/bench_conf_writers.pl new file mode 100644 index 0000000..37b1070 --- /dev/null +++ b/samples/benchmark/bench_conf_writers.pl @@ -0,0 +1,398 @@ +#!/usr/bin/perl -w + +use strict; +use vars qw($PLACEHOLDER); +use Benchmark qw(cmpthese timethese); +use CGI::Ex::Conf; +use POSIX qw(tmpnam); + +$PLACEHOLDER = chr(186).'~'.chr(186); + +my $n = -2; + +my $cob = CGI::Ex::Conf->new; +my %files = (); + +###----------------------------------------------------------------### + +# Rate yaml yaml2 sto pl xml g_conf ini sto2 +#yaml 250/s -- -1% -14% -14% -61% -77% -95% -95% +#yaml2 254/s 1% -- -13% -13% -60% -77% -95% -95% +#sto 292/s 17% 15% -- -0% -54% -73% -94% -95% +#pl 292/s 17% 15% 0% -- -54% -73% -94% -95% +#xml 636/s 155% 151% 118% 118% -- -42% -88% -88% +#g_conf 1088/s 335% 329% 273% 272% 71% -- -79% -80% +#ini 5144/s 1958% 1929% 1662% 1660% 708% 373% -- -3% +#sto2 5321/s 2029% 1999% 1723% 1721% 736% 389% 3% -- + +my $str = { + foo => {key1 => "bar", key2 => "ralph"}, + pass => {key1 => "word", key2 => "ralph"}, + garbage => {key1 => "can", key2 => "ralph"}, + mighty => {key1 => "ducks", key2 => "ralph"}, + quack => {key1 => "moo", key2 => "ralph"}, + one1 => {key1 => "val1", key2 => "ralph"}, + one2 => {key1 => "val2", key2 => "ralph"}, + one3 => {key1 => "val3", key2 => "ralph"}, + one4 => {key1 => "val4", key2 => "ralph"}, + one5 => {key1 => "val5", key2 => "ralph"}, + one6 => {key1 => "val6", key2 => "ralph"}, + one7 => {key1 => "val7", key2 => "ralph"}, + one8 => {key1 => "val8", key2 => "ralph"}, +}; + +###----------------------------------------------------------------### + +# Rate yaml yaml2 pl sto xml g_conf sto2 +#yaml 736/s -- -3% -20% -21% -62% -72% -89% +#yaml2 755/s 3% -- -18% -19% -61% -71% -89% +#pl 923/s 25% 22% -- -1% -53% -65% -86% +#sto 928/s 26% 23% 1% -- -53% -65% -86% +#xml 1961/s 166% 160% 113% 111% -- -26% -71% +#g_conf 2635/s 258% 249% 185% 184% 34% -- -61% +#sto2 6824/s 827% 803% 639% 635% 248% 159% -- + +#$str = { +# foo => "bar", +# pass => "word", +# garbage => "can", +# mighty => "ducks", +# quack => "moo", +# one1 => "val1", +# one2 => "val2", +# one3 => "val3", +# one4 => "val4", +# one5 => "val5", +# one6 => "val6", +# one7 => "val7", +# one8 => "val8", +#}; + +###----------------------------------------------------------------### + +my $conf = eval $str; + +my %TESTS = (); + +### do perl +my $dir = tmpnam; +mkdir $dir, 0755; +my $tmpnam = "$dir/bench"; +my $file = $tmpnam. '.pl'; +$TESTS{pl} = sub { + $cob->write_ref($file, $str); +}; +$files{pl} = $file; + +### do a generic conf_write +my $file2 = $tmpnam. '.g_conf'; +local $CGI::Ex::Conf::EXT_WRITERS{g_conf} = \&generic_conf_write; +$TESTS{g_conf} = sub { + $cob->write_ref($file2, $str); +}; +$files{g_conf} = $file2; + + +### load in the rest of the tests that we support +if (eval {require JSON}) { + my $_file = tmpnam(). '.json'; + $TESTS{json} = sub { + $cob->write_ref($file, $str); + }; + $files{json} = $_file; +} + +if (eval {require Storable}) { + my $_file = $tmpnam. '.sto'; + $TESTS{sto} = sub { + $cob->write_ref($file, $str); + }; + $files{sto} = $_file; +} + +if (eval {require Storable}) { + my $_file = $tmpnam. '.sto2'; + $TESTS{sto2} = sub { + &Storable::store($str, $_file); + }; + $files{sto2} = $_file; +} + +if (eval {require YAML}) { + my $_file = $tmpnam. '.yaml'; + $TESTS{yaml} = sub { + $cob->write_ref($_file, $str); + }; + $files{yaml} = $_file; +} + +if (eval {require YAML}) { + my $_file = $tmpnam. '.yaml2'; + $TESTS{yaml2} = sub { + &YAML::DumpFile($_file, $str); + }; + $files{yaml2} = $_file; +} + +if (eval {require Config::IniHash}) { + my $_file = $tmpnam. '.ini'; + $TESTS{ini} = sub { + local $^W = 0; + $cob->write_ref($_file, $str); + }; + $files{ini} = $_file; +} + +if (eval {require XML::Simple}) { + my $_file = $tmpnam. '.xml'; + $TESTS{xml} = sub { + $cob->write_ref($_file, $str); + }; + $files{xml} = $_file; +} + +### tell file locations +foreach my $key (sort keys %files) { + print "$key => $files{$key}\n"; +} + +foreach my $key (keys %TESTS) { + eval { &{ $TESTS{$key} } }; + if ($@) { + warn "Test for $key failed - skipping"; + delete $TESTS{$key}; + } +} + + +cmpthese timethese ($n, \%TESTS); + +### comment out this line to inspect files +unlink $_ foreach values %files; +rmdir $dir; + +###----------------------------------------------------------------### + +sub generic_conf_read { + my $_file = shift || die "No filename supplied"; + my $sep_by_newlines = ($_[0] && lc($_[0]) eq 'sep_by_newlines') ? 1 : 0; + + ### fh will now lose scope and close itself if necessary + my $FH = do { local *FH; *FH }; + open ($FH, $_file) || return {}; + + my $x = 0; + my $conf = {}; + my $key = ''; + my $val; + my $line; + my ($is_array,$is_hash,$is_multiline); + my $order; + $order = [] if wantarray; + + while( defined($line = <$FH>) ){ + last if ! defined $line; + last if $x++ > 10000; + + next if index($line,'#') == 0; + + if ($line =~ /^\s/ && ($is_multiline || $line ne "\n")){ + next if ! length($key); + $conf->{$key} .= $line; + $is_multiline = 1; + + }else{ + ### duplicate trim section + if( length($key) ){ + $conf->{$key} =~ s/\s+$//; + if( $is_array || $is_hash ){ + $conf->{$key} =~ s/^\s+//; + my $urldec = (index($conf->{$key},'%')>-1 || index($conf->{$key},'+')>-1); + my @pieces; + if ($sep_by_newlines) { + @pieces = split(/\s*\n\s*/,$conf->{$key}); + @pieces = map {split(/\s+/,$_,2)} @pieces if $is_hash; + } else { + @pieces = split(/\s+/,$conf->{$key}); + } + if( $urldec ){ + foreach my $_val (@pieces){ + $_val =~ y/+/ / if ! $sep_by_newlines; + $_val =~ s/%([a-f0-9]{2})/chr(hex($1))/egi; + } + } + if( $is_array ){ + foreach (@pieces){ $_="" if index($_,$PLACEHOLDER)>-1 } + $conf->{$key} = \@pieces; + }elsif( $is_hash ){ + foreach (@pieces){ $_="" if index($_,$PLACEHOLDER)>-1 } + shift(@pieces) if scalar(@pieces) % 2; + $conf->{$key} = {@pieces}; + } + }elsif( ! $is_multiline ){ + $conf->{$key} =~ y/+/ / if ! $sep_by_newlines; + $conf->{$key} =~ s/%([a-f0-9]{2})/chr(hex($1))/egi; + } + } + + ($key,$val) = split(/\s+/,$line,2); + $is_array = 0; + $is_hash = 0; + $is_multiline = 0; + if (! length($key)) { + next; + } elsif (index($key,'array:') == 0) { + $is_array = $key =~ s/^array://i; + } elsif (index($key,'hash:') == 0) { + $is_hash = $key =~ s/^hash://i; + } + $key =~ y/+/ / if ! $sep_by_newlines; + $key =~ s/%([a-f0-9]{2})/chr(hex($1))/egi; + $conf->{$key} = $val; + push @$order, $key if $order; + } + } + + ### duplicate trim section + if( length($key) && defined($conf->{$key}) ){ + $conf->{$key} =~ s/\s+$//; + if( $is_array || $is_hash ){ + $conf->{$key} =~ s/^\s+//; + my $urldec = (index($conf->{$key},'%')>-1 || index($conf->{$key},'+')>-1); + my @pieces; + if ($sep_by_newlines) { + @pieces = split(/\s*\n\s*/,$conf->{$key}); + @pieces = map {split(/\s+/,$_,2)} @pieces if $is_hash; + } else { + @pieces = split(/\s+/,$conf->{$key}); + } + if( $urldec ){ + foreach my $_val (@pieces){ + $_val =~ y/+/ / if ! $sep_by_newlines; + $_val =~ s/%([a-f0-9]{2})/chr(hex($1))/egi; + } + } + if( $is_array ){ + foreach (@pieces){ $_="" if index($_,$PLACEHOLDER)>-1 } + $conf->{$key} = \@pieces; + }elsif( $is_hash ){ + foreach (@pieces){ $_="" if index($_,$PLACEHOLDER)>-1 } + shift(@pieces) if scalar(@pieces) % 2; + $conf->{$key} = {@pieces}; + } + }elsif( ! $is_multiline ){ + $conf->{$key} =~ y/+/ / if ! $sep_by_newlines; + $conf->{$key} =~ s/%([a-f0-9]{2})/chr(hex($1))/egi; + } + } + + + close($FH); + return $order ? ($conf,$order) : $conf; +} + + +sub generic_conf_write{ + my $_file = shift || die "No filename supplied"; + + if (! @_) { + return; + } + + my $new_conf = shift || die "Missing update hashref"; + return if ! keys %$new_conf; + + + ### do we allow writing out hashes in a nice way + my $sep_by_newlines = ($_[0] && lc($_[0]) eq 'sep_by_newlines') ? 1 : 0; + + ### touch the file if necessary + if( ! -e $_file ){ + open(TOUCH,">$_file") || die "Conf file \"$_file\" could not be opened for writing: $!"; + close(TOUCH); + } + + ### read old values + my $conf = &generic_conf_read($_file) || {}; + my $key; + my $val; + + ### remove duplicates and undefs + while (($key,$val) = each %$new_conf){ + $conf->{$key} = $new_conf->{$key}; + } + + ### prepare output + my $output = ''; + my $qr = qr/([^\ \!\"\$\&-\*\,-\~])/; + foreach $key (sort keys %$conf){ + next if ! defined $conf->{$key}; + $val = delete $conf->{$key}; + $key =~ s/([^\ \!\"\$\&-\*\,-9\;-\~\/])/sprintf("%%%02X",ord($1))/eg; + $key =~ tr/\ /+/; + my $ref = ref($val); + if( $ref ){ + if( $ref eq 'HASH' ){ + $output .= "hash:$key\n"; + foreach my $_key (sort keys %$val){ + my $_val = $val->{$_key}; + next if ! defined $_val; + $_val =~ s/$qr/sprintf("%%%02X",ord($1))/ego; + $_key =~ s/$qr/sprintf("%%%02X",ord($1))/ego; + if ($sep_by_newlines) { + $_val =~ s/^(\s)/sprintf("%%%02X",ord($1))/ego; + $_val =~ s/(\s)$/sprintf("%%%02X",ord($1))/ego; + $_key =~ s/\ /%20/g; + } else { + $_val =~ tr/\ /+/; + $_key =~ tr/\ /+/; + } + $_val = $PLACEHOLDER if ! length($_val); + $output .= "\t$_key\t$_val\n"; + } + }elsif( $ref eq 'ARRAY' ){ + $output .= "array:$key\n"; + foreach (@$val){ + my $_val = $_; + $_val =~ s/$qr/sprintf("%%%02X",ord($1))/ego; + if ($sep_by_newlines) { + $_val =~ s/^(\s)/sprintf("%%%02X",ord($1))/ego; + $_val =~ s/(\s)$/sprintf("%%%02X",ord($1))/ego; + } else { + $_val =~ tr/\ /+/; + } + $_val = $PLACEHOLDER if ! length($_val); + $output .= "\t$_val\n"; + } + }else{ + $output .= "$key\tbless('$val','$ref')\n"; # stringify the ref + } + }else{ + if( $val =~ /\n/ ){ # multiline values that are indented properly don't need encoding + if( $val =~ /^\s/ || $val =~ /\s$/ || $val =~ /\n\n/ || $val =~ /\n([^\ \t])/ ){ + if ($sep_by_newlines) { + $val =~ s/([^\!\"\$\&-\~])/sprintf("%%%02X",ord($1))/eg; + } else { + $val =~ s/([^\ \!\"\$\&-\*\,-\~])/sprintf("%%%02X",ord($1))/eg; + $val =~ y/ /+/; + } + } + }else{ + $val =~ s/([^\ \t\!\"\$\&-\*\,-\~])/sprintf("%%%02X",ord($1))/eg; + $val =~ s/^(\s)/sprintf("%%%02X",ord($1))/eg; + $val =~ s/(\s)$/sprintf("%%%02X",ord($1))/eg; + } + $output .= "$key\t$val\n"; + } + } + + open (CONF,"+<$_file") || die "Could not open the file for writing ($_file) -- [$!]"; + print CONF $output; + truncate CONF, length($output); + close CONF; + + return 1; +} + +1; +