]>
git.p6c8.net - selfforum.git/blob - selfforum-cgi/shared/Posting/Cache.pm
1 package Posting
::Cache
;
3 ################################################################################
5 # File: shared/Posting/Cache.pm #
7 # Authors: André Malo <nd@o3media.de> #
9 # Description: Views/Voting Cache class #
11 ################################################################################
18 ################################################################################
26 sub VERSION
{(q
$Revision$ =~ /([\d.]+)\s*$/)[0] or '0.0'}
28 my $O_BINARY = eval q{
32 $O_BINARY = 0 if ($@);
34 ### sub new ####################################################################
38 # Params: $pathname - full qualified cache path
40 # Return: Posting::Cache object
43 my $self = bless {} => shift;
46 $self -> set_path (+shift);
51 ### sub clear_error ############################################################
53 # clear verbal error data
62 $self -> {verb_error} = undef;
67 sub error {$_[0]->{verb_error}}
72 $self -> {verb_error} = +shift;
76 ### sub set_path ###############################################################
80 # Params: $pathname - full qualified cache path
83 my ($self, $pathname) = @_;
85 $self -> {cachepath} = $pathname;
90 sub cachepath {$_[0] -> {cachepath}}
91 sub threaddir {$_[0] -> cachepath . $_[1] -> {thread}}
92 sub threadpath {$_[0] -> threaddir ($_[1]) . '/'}
93 sub cachefile {$_[0] -> threadpath ($_[1]) . $_[1] -> {posting} . '.txt'}
94 sub summaryfile {$_[0] -> cachepath . 'summary.bin'}
96 ### sub delete_threads #########################################################
98 # remove threads from cache
100 # Params: @threads - list of threadnumbers
102 # Return: Status Code (Bool)
105 my ($self, @threads) = @_;
106 my %threads = map {$_ => 1} @threads;
113 sub r_delete_threads {
114 my ($self, $handle, $threads) = @_;
115 my $l = length (pack 'L' => 0);
116 my $reclen = $l << 2;
117 my $len = -s $handle;
118 my $num = int ($len / $reclen) -1;
124 seek $handle, $_ * $reclen + $l, 0 or return;
125 read ($handle, $buf, $l) == $l or return;
126 if ($threads->{unpack 'L' => $buf}) {
127 seek $handle, $_ * $reclen + $l, 0 or return;
128 print $handle pack ('L' => 0) or return;
132 rmtree ($self->threaddir({thread => $_}), 0, 0)
133 for (keys %$threads);
138 ### sub garbage_collection #####################################################
140 # remove old entrys from the beginning of the cache
146 sub garbage_collection {
149 $self -> purge_wrap (
150 \&r_garbage_collection
153 sub r_garbage_collection {
154 my ($self, $handle, $file) = @_;
156 my $reclen = length (pack 'L', 0) << 2;
157 my $len = -s $handle;
158 my $num = int ($len / $reclen) -1;
159 my ($z, $buf, $h) = 0;
165 seek $handle, 0, 0 or return;
166 read ($handle, $buf, $len) or return;
168 (undef, $h) = (unpack 'L2' => substr ($buf, $_ * $reclen, $reclen));
170 return unless (defined $h);
173 substr ($buf, 0, $z * $reclen) = '';
175 seek $file, 0, 0 or return;
176 print $file $buf or return;
182 ### sub find_pos ($$) ##########################################################
184 # find position in cache file
186 # Params: $handle - summary file handle
187 # $posting - posting number
189 # Return: position or false (undef)
192 my ($handle, $posting) = @_;
193 my $reclen = length (pack 'L',0);
194 my $lreclen = $reclen << 2;
195 seek $handle, 0, 0 or return;
198 read ($handle, $buf, $reclen) == $reclen or return;
200 my $first = unpack ('L' => $buf);
201 $first <= $posting or return;
203 my $pos = ($posting - $first) * $lreclen;
204 seek $handle, $pos, 0 or return;
209 ### sub add_view ###############################################################
211 # increment the views-counter
213 # Params: hash reference
216 # Return: Status code (Bool)
219 my ($self, $param) = @_;
227 my ($self, $handle, $param) = @_;
228 my $reclen = length (pack 'L', 0) << 2;
230 defined ($pos = find_pos $handle, $param->{posting}) or return;
233 seek $handle, $pos, 0 or return;
234 read ($handle, $buf, $reclen) == $reclen or return;
236 my ($posting, $thread, $views, $votings) = unpack 'L4' => $buf;
237 $thread == 0xFFFFFFFF and $thread = $param->{thread};
239 $param->{thread} == $thread or return;
240 $param->{posting} == $posting or return;
242 seek $handle, $pos, 0 or return;
245 print $handle pack ('L4' => $posting, $thread, $views+1, $votings) or return;
250 ### sub pick ###################################################################
252 # read information of one posting
254 # Params: $param - hash reference
257 # Return: hash reference or false
260 my ($self, $param) = @_;
264 $self->cachefile($param),
270 my ($self, $handle, $file, $param) = @_;
271 my $reclen = 4 * length (pack 'L' => 0);
275 defined($pos = find_pos $handle, $param->{posting}) or return;
277 seek $handle, $pos, 0 or return;
278 read ($handle, $buf, $reclen) == $reclen or return;
280 my ($posting, $thread, $views, $votings) = unpack 'L4' => $buf;
281 $thread == 0xFFFFFFFF and $thread = $param->{thread};
283 $param->{thread} == $thread or return;
284 $param->{posting} == $posting or return;
286 seek $file, 0, 0 or return;
287 my @records = <$file>;
297 time => $_->[0] || 0,
300 } [split ' ' => $_,3]
309 ### sub summary ################################################################
311 # read out the cache and return a summary
315 # Return: hash reference or false
320 $self -> read_wrap (\&r_summary)
325 my ($self, $handle) = @_;
326 my $reclen = length (pack 'L', 0) << 2;
327 my $len = -s $handle;
328 my $num = int ($len / $reclen) -1;
332 seek $handle, 0, 0 or return;
333 read ($handle, $buf, $len) or return;
335 my ($posting, $thread, $views, $votings)
336 = (unpack 'L4' => substr ($buf, $_ * $reclen, $reclen));
338 $hash{$thread} = {} unless $hash{$thread};
339 $hash{$thread} -> {$posting} = {
345 $self -> {summary} = \%hash;
351 ### sub add_voting #############################################################
355 # Params: $param - hash reference
356 # (thread, posting, IP, ID, time)
358 # Return: Status code (Bool)
361 my ($self, $param) = @_;
369 my ($self, $handle, $file, $param) = @_;
370 my $reclen = length (pack 'L', 0) << 2;
372 defined ($pos = find_pos $handle, $param->{posting}) or return;
375 seek $handle, $pos, 0 or return;
376 read ($handle, $buf, $reclen) == $reclen or return;
378 my ($posting, $thread, $views, $votings) = unpack 'L4' => $buf;
379 $thread == 0xFFFFFFFF and $thread = $param->{thread};
381 $param->{thread} == $thread or return;
385 seek $file, 0, 2 or return;
387 join (' ' => $param->{time}, $param->{IP}, $param->{ID}) or return;
392 seek $handle, $pos, 0 or return;
394 pack ('L4' => $posting, $thread, $views, $votings+1) or return;
400 ### sub add_posting ############################################################
402 # add an empty cache entry of a posting
404 # Params: $param - hash reference
407 # Return: Status code (Bool)
417 my ($self, $handle, $param) = @_;
418 my $newfile = new Lock ($self->cachefile($param));
421 unless (-d $self -> threaddir($param)) {
422 mkdir $self->threaddir($param), 0777 or return;
424 $newfile -> open (O_WRONLY | O_CREAT | O_TRUNC) or return;
425 $newfile -> close or return;
429 my $reclen = length (pack 'L' => 0) << 2;
430 seek $handle, 0-$reclen, 2 or return;
432 read ($handle, $buf, $reclen) == $reclen or return;
433 $z = unpack 'L' => $buf;
434 if ($z < $param->{posting}) {
435 while (++$z < $param->{posting}) {
436 seek $handle, 0, 2 or return;
438 'L4' => $z, 0xFFFFFFFF, 0, 0
446 $pos = find_pos $handle, $param->{posting}
448 seek $handle, $pos, 0 or return;
452 unless (defined $z) {
453 seek $handle, 0, 2 or return;
457 'L4' => $param->{posting}, $param->{thread}, 0, 0
465 ### sub add_wrap ################################################################
467 # file lock, open, execute, close, unlock wrapper
468 # for adding an empty entry
470 # Params: $gosub - sub reference (for execution)
471 # @param - params (for $gosub)
473 # Return: Status code (Bool)
476 my ($self, $gosub, @param) = @_;
478 my $summary = new Lock ($self -> summaryfile);
480 unless ($summary -> lock (LH_EXCL)) {
481 $self->set_error ('could not write-lock summary file '.$summary -> filename);
484 unless ($summary -> open($O_BINARY | O_APPEND | O_CREAT | O_RDWR)) {
486 ('could not open to read/write/append summary file '.$summary->filename);
489 $status = $gosub -> (
494 unless ($summary -> close) {
496 $self->set_error('could not close summary file '.$summary -> filename);
506 ### sub vote_wrap ###############################################################
508 # file lock, open, execute, close, unlock wrapper
511 # Params: $gosub - sub reference (for execution)
512 # @param - params (for $gosub)
514 # Return: Status code (Bool)
517 my ($self, $gosub, $param) = @_;
519 my $summary = new Lock ($self -> summaryfile);
521 unless ($summary -> lock (LH_EXCL)) {
522 $self->set_error ('could not write-lock summary file '.$summary -> filename);
525 unless ($summary -> open (O_RDWR | $O_BINARY)) {
526 $self->set_error ('could not open to read/write summary file '.$summary -> filename);
529 unless (-d $self->threaddir($param)) {
530 mkdir $self->threaddir($param), 0777 or return;
532 my $cache = new Lock ($self->cachefile($param));
534 unless ($cache -> lock (LH_EXCL)) {
535 $self->set_error ('could not write-lock cache file '.$cache -> filename);
538 unless ($cache -> open (O_APPEND | O_CREAT | O_RDWR)) {
539 $self->set_error ('could not open to read/write/append cache file '.$cache -> filename);
542 $status = $gosub -> (
548 unless ($cache -> close) {
550 $self->set_error('could not close cache file '.$cache -> filename);
555 unless ($summary -> close) {
557 $self->set_error('could not close summary file '.$summary -> filename);
567 ### sub purge_wrap ##############################################################
569 # file lock, open, execute, close, unlock wrapper
570 # for garbage collection
572 # Params: $gosub - sub reference (for execution)
573 # @param - params (for $gosub)
575 # Return: Status code (Bool)
578 my ($self, $gosub, @param) = @_;
580 my $summary = new Lock ($self -> summaryfile);
582 unless ($summary -> lock (LH_EXSH)) {
583 $self->set_error ('could not write-lock summary file '.$summary -> filename);
586 my $temp = new Lock::Handle ($summary -> filename . '.temp');
587 unless ($temp -> open (O_CREAT | O_WRONLY | O_TRUNC | $O_BINARY)) {
588 $self->set_error ('could not open to write temp summary file '.$temp -> filename);
591 unless ($summary -> open (O_RDONLY | $O_BINARY)) {
592 $self->set_error ('could not open to read summary file '.$summary -> filename);
595 $status = $gosub -> (
601 unless ($summary -> close) {
603 $self->set_error('could not close summary file '.$summary -> filename);
606 unless ($temp -> close) {
608 $self->set_error('could not close temp summary file '.$temp -> filename);
610 unless ($summary -> lock (LH_EXCL)) {
612 $self->set_error ('could not write-lock summary file '.$summary -> filename);
615 unless (rename $temp -> filename => $summary -> filename) {
617 $self->set_error('could not rename temp summary file '.$temp -> filename);
628 ### sub pick_wrap ###############################################################
630 # file lock, open, execute, close, unlock wrapper
631 # for picking a posting
633 # Params: $gosub - sub reference (for execution)
634 # @param - params (for $gosub)
636 # Return: Status code (Bool)
639 my ($self, $gosub, $filename, @param) = @_;
641 my $cache = new Lock ($filename);
643 unless ($cache -> lock (LH_SHARED)) {
644 $self->set_error ('could not lock cache file '.$cache -> filename);
647 unless ($cache -> open (O_RDONLY)) {
648 $self->set_error ('could not open to read cache file '.$cache -> filename);
651 $status = $self -> read_wrap (
656 unless ($cache -> close) {
658 $self->set_error('could not close cache file '.$cache -> filename);
668 ### sub read_wrap ###############################################################
670 # file lock, open, execute, close, unlock wrapper
671 # for reading of summary file
673 # Params: $gosub - sub reference (for execution)
674 # @param - params (for $gosub)
676 # Return: Status code (Bool)
679 my ($self, $gosub, @param) = @_;
681 my $summary = new Lock ($self -> summaryfile);
683 unless ($summary -> lock (LH_SHARED)) {
684 $self->set_error ('could not read-lock summary file '.$summary -> filename);
687 unless ($summary -> open (O_RDONLY | $O_BINARY)) {
688 $self->set_error ('could not open to read summary file '.$summary -> filename);
691 $status = $gosub -> (
696 unless ($summary -> close) {
698 $self->set_error('could not close summary file '.$summary -> filename);
708 ### sub mod_wrap ################################################################
710 # file lock, open, execute, close, unlock wrapper
711 # for modification of summary file
713 # Params: $gosub - sub reference (for execution)
714 # @param - params (for $gosub)
716 # Return: Status code (Bool)
719 my ($self, $gosub, @param) = @_;
721 my $summary = new Lock ($self -> summaryfile);
723 unless ($summary -> lock (LH_EXCL)) {
724 $self->set_error ('could not write-lock summary file '.$summary -> filename);
727 unless ($summary -> open (O_RDWR | $O_BINARY)) {
728 $self->set_error ('could not open to read/write summary file '.$summary -> filename);
731 $status = $gosub -> (
736 unless ($summary -> close) {
738 $self->set_error('could not close summary file '.$summary -> filename);
748 # keep 'require' happy
754 ### end of Posting::Cache ######################################################
patrick-canterino.de