/usr/local/CPAN/CDB_Perl/CDB_Perl/Write.pm


package CDB_Perl::Write;

use Carp qw(carp croak);

require CDB_Perl;
@ISA = qw(CDB_Perl);

use strict;
#use warnings;

sub new{
	my $pack = shift;
	my $fname = shift or croak "$pack->new(filename); filename not defined";

	my $self =  bless{}, $pack;
	$self->file_open($fname, '>');
	$self->{'table'}=[];
	$self->seek(2048);
	return $self;
}

sub insert{
	my ($self, $k,$v) = @_;

	if($self->{'finished'}){
		croak "Trying to insert values into a finished CDB."
	}else{
		my $table = $self->{'table'};
		my $pos = $self->{'pos'};

		if(!defined($k)){
			croak "insert must be called with 'key', 'value' as arguments. Key not defined.";
		}
		my $klen = length($k);
		my $vlen = length($v);
		my ($h, $h0,$h1) = $self->hash($k);

		if(!$table->[$h0]){
			$table->[$h0] = [];
		}

		push @{$table->[$h0]},($h,$pos);

		$self->write_long($klen,$vlen);
		$self->write($k.$v,$klen+$vlen);

		return $self;
	}
}

sub finish{
	my $self = shift;
	if($self->{'finished'}){
		croak("Trying to finish an already finished CDB.");
	}else{
		my $table = $self->{'table'};
		my $pos = $self->{'pos'};
		my $init = $pos;

		my @head;

		for my $n (0..255){
			my $t = $table->[$n];
			my $len = 0;
			if($t && @$t){
				$len = @$t;
				$pos = $self->{'pos'};
				$self->write_table($n);
			}
			push @head,($pos,$len);
		}
		$self->seek(0);
		$self->write_long(@head);
		$self->seek($init);
		$self->{'finished'} = 1;
		return $self;
	}
}

sub write_table{
	my ($self, $n) = @_;
	die unless defined($n);

	my $table = $self->{'table'}->[$n];
	my $len = @$table;
	
	my @tmp;
	#init table
	$#tmp = $len*2-1;
	for(0 .. $len*2-1){
		$tmp[$_] = 0;
	}

	for my $i (0..$len/2-1){
		my $hash = $table->[$i*2];
		my $pos  = $table->[$i*2 + 1];
		
		my $h = ($hash>>8) % $len;

		#find next free slot;
		my $ii= $h*2;
		while($tmp[$ii+1] != 0){
			$ii = ($ii + 2) % (2*$len);
		}
		@tmp[$ii,$ii+1] = ($hash,$pos);
	}
	
	$self->write_long(@tmp);
	return $self;
}

sub write_long{
	my $self = shift;
	my @data = @_;

	my $t = pack("V".(scalar @data),@data);
	$self->write($t,4*@data);
	return $self;
}

sub write{
	my $self = shift;
	my($data, $len) = @_;

	my $file = $self->{'file'};
	if(!print $file $data){
		croak("IO error $!");
	}

	if(!defined ($len)){
		$len = length($data);
	}
	$self->{'pos'}+=$len;
	return $self;
}

sub TIEHASH{
	my $pack = shift;
	return $pack->new(@_);
}

sub FETCH{
	my ($self, $key) = @_;
	return;
	croak "Can't read data on an CDB writer $key";
}

sub STORE{
	my ($self, $key, $value) = @_;
	return $self->insert($key, $value);
}

sub UNTIE{
	shift->finish;
}

sub DELETE{
	croak "Can't remove values once inserted";
}

sub DESTROY{
	my $self = shift;
	if(!$self->{'finished'}){
		$self->finish;
	}
	close($self->{'file'}) or croak "Error closing CDB file.\n$!";
}

1;