pl: Tester gesucht

Beitrag lesen

problematische Seite

Hallo,

hab einen neuen Serializer entwickelt, derzeit in Testphase für Sessiondateien. Wer Lust, Zeit und Interesse hat, möge mal den damit realisierten Login/Logout testen, siehe "Problematische Seite" Link. Also mal ein paarmal ein/ausloggen.

Es soll nicht umsonst sein, Algorithmus und Source lege ich offen, siehe untenstehend.

MfG

package FreezeHash;

# Recursive serialize for hash of hashes
# ALGORITHMUS
# Die Datenstruktur wird als Baum aufgefasst und rekursiv durchlaufen
# Dabei wird jeder Eintrag erfasst und bekommt eine eigene Entity-ID
# als fortlaufende Nummer. Im Ergebnis dessen entsteht eine lineare
# Struktur: ein einfacher Hash of Hashes nach dem Muster 
# Entity/Attribute/Value kurz EAV
# In der linearisierten Struktur ist für jeden Knoten der parent
# als Attribut hinzugefügt und damit kann die Original-
# Datenstruktur wiederhergestellt werden.

use strict;
use warnings;
use IO::File;
use Fcntl qw(:flock);
use Carp;
use bytes;

#use Data::Dumper;
#$Data::Dumper::Sortkeys = 1;



sub new{
    my $class = shift;
    my %cfg = (
        file => '', # full qualified
        lock => 0,  # atomar read+write
        auto => 0,  # auto write to file
    @_);    

    return eval{
        my $fh = IO::File->new;
        $fh->open( $cfg{file}, O_CREAT|O_BINARY|O_RDWR) ||
            croak "Error open file '$cfg{file}': $!";

        if( $cfg{lock} ){
            flock($fh, LOCK_EX) || 
                carp "Your system does'nt support flock!";
        }
        bless{
            lfdnr => 1,    # root entity
            EAV   => {},
            FH    => $fh ,
            CFG   => \%cfg # for debug
        }, $class;
    };
}

# Linearisierung der geschachtelten Datenstruktur
sub freeze{
    my $self   = shift;
    my $ds     = shift;
    my $parent = shift || $self->{lfdnr};

    # Hash wird rekursiv durchlaufen und linearisiert
    # Jeder Eintrag bekommt eine fortlaufende Nummer
    foreach my $key( keys %$ds ){
        if( ref $ds->{$key} eq 'HASH' ){
            my $ent = $self->_lfdnr(1);
            $self->{EAV}{$ent} = {
                type   => 'HASH',
                att    => $key,
                parent => $parent,
                ent    => $ent
            };
            $self->freeze( $ds->{$key} );
        }
        else{
            my $ent = $self->_lfdnr(1);
            $self->{EAV}{$ent} = {
                parent => $parent,
                type   => 'STRING',
                att    => $key,
                val    => $ds->{$key},
                ent    => $ent
            };
        }
    }

    # ab hier kann serialisiert werden
    # serialisiert wird beim write()-Aufruf
}

# Datenstruktur wiederherstellen
sub thaw{
    my $self = shift;

    # aus Datei deserialisieren, Ergebnis ist
    # eine linearisierte Datenstruktur
    $self->_read;    
    
    # die ursprüngliche Datenstruktur wiederherstellen
    $self->{RESTORED} = {};
    
    # Knoten direkt unterhalb der Wurzel haben entity 1
    foreach my $root( @{$self->{CHILDREN}{1}} ){
        if( $self->{EAV}{$root}{type} eq 'STRING' ){
            $self->{RESTORED}{$self->{EAV}{$root}{att}} = $self->{EAV}{$root}{val};
        }
        else{
            $self->{RESTORED}{$self->{EAV}{$root}{att}} = {};
            foreach my $child ( @{$self->{CHILDREN}{$root}} ){
                $self->_restore($self->{RESTORED}{$self->{EAV}{$root}{att}}, $self->{EAV}{$child});
            }
        }
    }

    return $self->{RESTORED};
}

# Daten in das Handle serialisieren
sub write{
    my $self = shift;
    # STRING kann undef sein, hierzu wird ein byte vergeben 0|1
    # zur Kennzeichnung undef oder String
    $self->{FH}->seek(0,0);
    $self->{FH}->truncate(0);

    foreach my $ent( keys %{$self->{EAV}} ){
        foreach my $att( keys %{$self->{EAV}{$ent}} ){
            my $def = $self->{EAV}{$ent}{$att} ? 1 : 0;
            my $val = $def ? $self->{EAV}{$ent}{$att} : '';
            
            # pack with little endians
            $self->{FH}->print(
                pack('VVV', length $ent, length $att, length $val).$def.$ent.$att.$val
            );
        }    
    }    
    
    return 1;
}

sub DESTROY{
    my $self = shift;
    if( $self->{CFG}{auto} ){ $self->write }
    $self->{FH}->close;
}

#################################### Private ##############################
# wird rekursiv aufgerufen, aus der linearen EAV Struktur
# das Original wiederherstellen
sub _restore{
    my $self = shift;
    my $href = shift; # aktueller stub
    my $hunt = shift; # hash der angefügt werden soll

    if( $hunt->{type} eq 'STRING' ){
        $href->{$hunt->{att}} = $hunt->{val};
    }
    else{
        # hier der rekursive Aufruf
        $href->{$hunt->{att}} = {}; 
        foreach my $child( @{$self->{CHILDREN}{$hunt->{ent}}} ){
            $self->_restore($href->{$hunt->{att}}, $self->{EAV}{$child});
        }
    }
}

# Lese Dateihandle und deserialize
sub _read{
    my $self = shift;
    $self->{FH}->seek(0,0);
    $self->{EAV} = {};
    
    my %CHLD = (); # parent-children relation
    while( read($self->{FH}, my $buffer, 12) ){
        my($elen,$alen,$vlen) = unpack 'VVV', $buffer;
        read($self->{FH}, my $def, 1);
        read($self->{FH}, my $ent, $elen);
        read($self->{FH}, my $att, $alen);
        read($self->{FH}, my $val, $vlen);

        # Korrektur wenn value = undef
        $val = undef if $def eq '0' && $att eq 'val';
        $self->{EAV}{$ent}{$att} = $val;
        
        if( $att eq 'parent' ){
            push @{$CHLD{$val}}, $ent;
        }
    }
    $self->{CHILDREN} = \%CHLD;
}

# Laufende Nummer zur Verwaltung der eigenen Entities
sub _lfdnr{
    my $self = shift;
    my $countup = shift || 0;
    return $countup ? ++$self->{lfdnr} : $self->{lfdnr};
}
1;#########################################################################
__END__
use strict;
use warnings;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;

# data struct
my $ds = {
    name => 'boo',
    nixdef => undef,
    addr => {
        addr_name  => 'foo',
        addr_vname => 'bar'
    },
    base => {
        base_addr => {
            base_addr_name  => 'foo',
            base_addr_vname => 'bar',
            base_addr_undef => undef,
            base_addr_hash => { base_addr_hash_name => 'otto' },
        },
    },
    #env => \%ENV
};


my $fr = FreezeHash->new( file => 'freeze.bin' ) or die $@;
$fr->freeze( $ds );
$fr->write or die $@;

my $res = $fr->thaw;

print Dumper $res,$ds;