602
|
1 |
# Copyright (c) 2001-2002 Joshua Chamas, Chamas Enterprises Inc. All rights reserved.
|
|
2 |
# Sponsored by development on NodeWorks http://www.nodeworks.com and Apache::ASP
|
|
3 |
# http://www.apache-asp.org
|
|
4 |
#
|
|
5 |
# This program is free software; you can redistribute it
|
|
6 |
# and/or modify it under the same terms as Perl itself.
|
|
7 |
|
|
8 |
package MLDBM::Sync::SDBM_File;
|
|
9 |
$VERSION = .17;
|
|
10 |
|
|
11 |
use SDBM_File;
|
|
12 |
use strict;
|
|
13 |
use vars qw(@ISA $MaxSegments $MaxSegmentLength %KEYS $Zlib $VERSION);
|
|
14 |
|
|
15 |
@ISA = qw(SDBM_File);
|
|
16 |
$MaxSegments = 8192; # to a 1M limit
|
|
17 |
# leave room for key index pad
|
|
18 |
$MaxSegmentLength = 128;
|
|
19 |
eval "use Compress::Zlib";
|
|
20 |
$Zlib = $@ ? 0 : 1;
|
|
21 |
|
|
22 |
sub FETCH {
|
|
23 |
my($self, $key) = @_;
|
|
24 |
my $segment_length = $MaxSegmentLength;
|
|
25 |
|
|
26 |
my $total_rv;
|
|
27 |
for(my $index = 0; $index < $MaxSegments; $index++) {
|
|
28 |
my $rv = $self->SUPER::FETCH(_index_key($key, $index));
|
|
29 |
if(defined $rv) {
|
|
30 |
$total_rv ||= '';
|
|
31 |
$total_rv .= $rv;
|
|
32 |
last if length($rv) < $segment_length;
|
|
33 |
} else {
|
|
34 |
last;
|
|
35 |
}
|
|
36 |
}
|
|
37 |
|
|
38 |
if(defined $total_rv) {
|
|
39 |
$total_rv =~ s/^(..)//s;
|
|
40 |
my $type = $1;
|
|
41 |
if($type eq 'G}') {
|
|
42 |
$total_rv = uncompress($total_rv);
|
|
43 |
} elsif ($type eq 'N}') {
|
|
44 |
# nothing
|
|
45 |
} else {
|
|
46 |
# old SDBM_File ?
|
|
47 |
$total_rv = $type . $total_rv;
|
|
48 |
}
|
|
49 |
}
|
|
50 |
|
|
51 |
$total_rv;
|
|
52 |
}
|
|
53 |
|
|
54 |
sub STORE {
|
|
55 |
my($self, $key, $value) = @_;
|
|
56 |
my $segment_length = $MaxSegmentLength;
|
|
57 |
|
|
58 |
# DELETE KEYS FIRST
|
|
59 |
for(my $index = 0; $index < $MaxSegments; $index++) {
|
|
60 |
my $index_key = _index_key($key, $index);
|
|
61 |
my $rv = $self->SUPER::FETCH($index_key);
|
|
62 |
if(defined $rv) {
|
|
63 |
$self->SUPER::DELETE($index_key);
|
|
64 |
} else {
|
|
65 |
last;
|
|
66 |
}
|
|
67 |
last if length($rv) < $segment_length;
|
|
68 |
}
|
|
69 |
|
|
70 |
# G - Gzip compression
|
|
71 |
# N - No compression
|
|
72 |
#
|
|
73 |
my $old_value = $value;
|
|
74 |
$value = ($Zlib && (length($value) >= $segment_length/2)) ? "G}".compress($value) : "N}".$value;
|
|
75 |
|
|
76 |
my($total_rv, $last_index);
|
|
77 |
for(my $index = 0; $index < $MaxSegments; $index++) {
|
|
78 |
if($index == $MaxSegments) {
|
|
79 |
die("can't store more than $MaxSegments segments of $MaxSegmentLength bytes per key in ".__PACKAGE__);
|
|
80 |
}
|
|
81 |
$value =~ s/^(.{0,$segment_length})//so;
|
|
82 |
my $segment = $1;
|
|
83 |
|
|
84 |
last if length($segment) == 0;
|
|
85 |
# print "STORING "._index_key($key, $index)." $segment\n";
|
|
86 |
my $rv = $self->SUPER::STORE(_index_key($key, $index), $segment);
|
|
87 |
$total_rv .= $segment;
|
|
88 |
$last_index = $index;
|
|
89 |
}
|
|
90 |
|
|
91 |
# use Time::HiRes;
|
|
92 |
# print "[".&Time::HiRes::time()."] STORED ".($last_index+1)." segments for length ".
|
|
93 |
# length($total_rv)." bytes for value ".length($old_value)."\n";
|
|
94 |
|
|
95 |
$old_value;
|
|
96 |
}
|
|
97 |
|
|
98 |
sub DELETE {
|
|
99 |
my($self, $key) = @_;
|
|
100 |
my $segment_length = $MaxSegmentLength;
|
|
101 |
|
|
102 |
my $total_rv;
|
|
103 |
for(my $index = 0; $index < $MaxSegments; $index++) {
|
|
104 |
my $index_key = _index_key($key, $index);
|
|
105 |
my $rv = $self->SUPER::FETCH($index_key) || '';
|
|
106 |
$self->SUPER::DELETE($index_key);
|
|
107 |
$total_rv ||= '';
|
|
108 |
$total_rv .= $rv;
|
|
109 |
last if length($rv) < $segment_length;
|
|
110 |
}
|
|
111 |
|
|
112 |
$total_rv =~ s/^(..)//s;
|
|
113 |
my $type = $1;
|
|
114 |
if($type eq 'G}') {
|
|
115 |
$total_rv = uncompress($total_rv);
|
|
116 |
} elsif ($type eq 'N}') {
|
|
117 |
# normal
|
|
118 |
} else {
|
|
119 |
# old SDBM_File
|
|
120 |
$total_rv = $type.$total_rv;
|
|
121 |
}
|
|
122 |
|
|
123 |
$total_rv;
|
|
124 |
}
|
|
125 |
|
|
126 |
sub FIRSTKEY {
|
|
127 |
my $self = shift;
|
|
128 |
|
|
129 |
my $key = $self->SUPER::FIRSTKEY();
|
|
130 |
my @keys = ();
|
|
131 |
if (defined $key) {
|
|
132 |
do {
|
|
133 |
if($key !~ /\*\*\d+$/s) {
|
|
134 |
if(my $new_key = _decode_key($key)) {
|
|
135 |
push(@keys, $new_key);
|
|
136 |
}
|
|
137 |
}
|
|
138 |
} while($key = $self->SUPER::NEXTKEY($key));
|
|
139 |
}
|
|
140 |
$KEYS{$self} = \@keys;
|
|
141 |
|
|
142 |
$self->NEXTKEY;
|
|
143 |
}
|
|
144 |
|
|
145 |
sub NEXTKEY {
|
|
146 |
my $self = shift;
|
|
147 |
shift(@{$KEYS{$self}});
|
|
148 |
}
|
|
149 |
|
|
150 |
sub _index_key {
|
|
151 |
my($key, $index) = @_;
|
|
152 |
$key =~ s/([\%\*])/uc sprintf("%%%02x",ord($1))/esg;
|
|
153 |
$index ? $key.'**'.$index : $key;
|
|
154 |
}
|
|
155 |
|
|
156 |
sub _decode_key {
|
|
157 |
my $key = shift;
|
|
158 |
$key =~ s/%([0-9a-fA-F]{2})/pack("c",hex($1))/ge;
|
|
159 |
$key;
|
|
160 |
}
|
|
161 |
|
|
162 |
1;
|
|
163 |
|