1#-------------------------------------------------------------------------------------------------
2# Perl binding of Kyoto Cabinet
3#                                                                Copyright (C) 2009-2010 FAL Labs
4# This file is part of Kyoto Cabinet.
5# This program is free software: you can redistribute it and/or modify it under the terms of
6# the GNU General Public License as published by the Free Software Foundation, either version
7# 3 of the License, or any later version.
8# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10# See the GNU General Public License for more details.
11# You should have received a copy of the GNU General Public License along with this program.
12# If not, see <http://www.gnu.org/licenses/>.
13#-------------------------------------------------------------------------------------------------
14
15
16package KyotoCabinet;
17
18use strict;
19use warnings;
20
21require Exporter;
22require XSLoader;
23use base qw(Exporter);
24our $VERSION = '1.20';
25XSLoader::load('KyotoCabinet', $VERSION);
26
27
28
29package KyotoCabinet::Error;
30
31use overload q("") => \&string;
32use overload "<=>" => \&compare;
33
34
35sub new {
36    my ($cls, $code, $message) = @_;
37    my $self = [SUCCESS(), "error"];
38    if (defined($code) && defined($message)) {
39        $self->[0] = $code;
40        $self->[1] = $message;
41    }
42    bless $self;
43    return $self;
44}
45
46
47sub set {
48    my ($self, $code, $message) = @_;
49    $self->[0] = $code;
50    $self->[1] = $message;
51    return undef;
52}
53
54
55sub code {
56    my ($self) = @_;
57    return $self->[0];
58}
59
60
61sub name {
62    my ($self) = @_;
63    return err_codename($self->[0]);
64}
65
66
67sub message {
68    my ($self) = @_;
69    return $self->[1];
70}
71
72
73sub string {
74    my ($self) = @_;
75    return sprintf("%s: %s", $self->name, $self->message);
76}
77
78
79sub compare {
80    my ($self, $right) = @_;
81    $right = $right->code if (ref($right) eq __PACKAGE__);
82    return $self->code <=> $right;
83}
84
85
86
87package KyotoCabinet::Visitor;
88
89
90sub new {
91    my ($cls) = @_;
92    my $self = {};
93    bless $self;
94    return $self;
95}
96
97
98sub visit_full {
99    my ($self, $key, $value) = @_;
100    return $self->NOP;
101}
102
103
104sub visit_empty {
105    my ($self, $key) = @_;
106    return $self->NOP;
107}
108
109
110
111package KyotoCabinet::FileProcessor;
112
113sub new {
114    my ($cls) = @_;
115    my $self = {};
116    bless $self;
117    return $self;
118}
119
120
121sub process {
122    my ($self, $path, $count, $size) = @_;
123    return 1;
124}
125
126
127
128package KyotoCabinet::Cursor;
129
130use overload q("") => \&string;
131
132
133sub new {
134    my ($cls, $db) = @_;
135    my $self = [0, undef];
136    $self->[0] = cur_new($db->[0]);
137    $self->[1] = $db;
138    bless $self;
139    return $self;
140}
141
142
143sub DESTROY {
144    my ($self) = @_;
145    cur_delete($self->[0]);
146    return undef;
147}
148
149
150sub disable {
151    my ($self) = @_;
152    cur_disable($self->[0]);
153    $self->[0] = 0;
154    return undef;
155}
156
157
158sub accept {
159    my ($self, $visitor, $writable, $step) = @_;
160    $writable = 1 if (!defined($writable));
161    $step = 0 if (!defined($step));
162    return cur_accept($self->[0], $visitor, $writable, $step);
163}
164
165
166sub set_value {
167    my ($self, $value, $step) = @_;
168    return cur_set_value($self->[0], $value, $step);
169}
170
171
172sub remove {
173    my ($self) = @_;
174    return cur_remove($self->[0]);
175}
176
177
178sub get_key {
179    my ($self, $step) = @_;
180    $step = 0 if (!defined($step));
181    return cur_get_key($self->[0], $step);
182}
183
184
185sub get_value {
186    my ($self, $step) = @_;
187    $step = 0 if (!defined($step));
188    return cur_get_value($self->[0], $step);
189}
190
191
192sub get {
193    my ($self, $step) = @_;
194    $step = 0 if (!defined($step));
195    return cur_get($self->[0], $step);
196}
197
198
199sub seize {
200    my ($self) = @_;
201    return cur_seize($self->[0]);
202}
203
204
205sub jump {
206    my ($self, $key) = @_;
207    return cur_jump($self->[0], $key);
208}
209
210
211sub jump_back {
212    my ($self, $key) = @_;
213    return cur_jump_back($self->[0], $key);
214}
215
216
217sub step {
218    my ($self) = @_;
219    return cur_step($self->[0]);
220}
221
222
223sub step_back {
224    my ($self) = @_;
225    return cur_step_back($self->[0]);
226}
227
228
229sub db {
230    my ($self) = @_;
231    return $self->[1];
232}
233
234
235sub error {
236    my ($self) = @_;
237    return $self->[1]->error;
238}
239
240
241sub string {
242    my ($self) = @_;
243    my $db = $self->[1];
244    my $path = $db->path;
245    $path = "(undef)" if (!defined($path));
246    my $key = $self->get_key;
247    $key = "(undef)" if (!defined($key));
248    return sprintf("%s: %s", $path, $key);
249}
250
251
252
253package KyotoCabinet::DB;
254
255use overload q("") => \&string;
256
257
258sub new {
259    my ($cls) = @_;
260    my $self = [0, undef, undef];
261    $self->[0] = db_new();
262    bless $self;
263    return $self;
264}
265
266
267sub DESTROY {
268    my ($self) = @_;
269    db_delete($self->[0]);
270    return undef;
271}
272
273
274sub error {
275    my ($self) = @_;
276    my ($code, $message) = db_error($self->[0]);
277    return KyotoCabinet::Error->new($code, $message);
278}
279
280
281sub open {
282    my ($self, $path, $mode) = @_;
283    $path = ":" if (!defined($path));
284    $mode = OWRITER() | OCREATE() if (!defined($mode) || $mode < 1);
285    return db_open($self->[0], $path, $mode);
286}
287
288
289sub close {
290    my ($self) = @_;
291    return db_close($self->[0]);
292}
293
294
295sub accept {
296    my ($self, $key, $visitor, $writable) = @_;
297    $writable = 1 if (!defined($writable));
298    return db_accept($self->[0], $key, $visitor, $writable);
299}
300
301
302sub accept_bulk {
303    my ($self, $keys, $visitor, $writable) = @_;
304    return 0 if (ref($keys) ne 'ARRAY');
305    $writable = 1 if (!defined($writable));
306    foreach my $key (@$keys) {
307        return 0 if (!db_accept($self->[0], $key, $visitor, $writable));
308    }
309    return 1;
310}
311
312
313sub iterate {
314    my ($self, $visitor, $writable) = @_;
315    $writable = 1 if (!defined($writable));
316    return db_iterate($self->[0], $visitor, $writable);
317}
318
319
320sub set {
321    my ($self, $key, $value) = @_;
322    return db_set($self->[0], $key, $value);
323}
324
325
326sub add {
327    my ($self, $key, $value) = @_;
328    return db_add($self->[0], $key, $value);
329}
330
331
332sub replace {
333    my ($self, $key, $value) = @_;
334    return db_replace($self->[0], $key, $value);
335}
336
337
338sub append {
339    my ($self, $key, $value) = @_;
340    return db_append($self->[0], $key, $value);
341}
342
343
344sub increment {
345    my ($self, $key, $num, $orig) = @_;
346    $num = 0 if (!defined($num));
347    $orig = 0 if (!defined($orig));
348    return db_increment($self->[0], $key, $num, $orig);
349}
350
351
352sub increment_double {
353    my ($self, $key, $num, $orig) = @_;
354    $num = 0 if (!defined($num));
355    $orig = 0 if (!defined($orig));
356    return db_increment_double($self->[0], $key, $num, $orig);
357}
358
359
360sub cas {
361    my ($self, $key, $oval, $nval) = @_;
362    return db_cas($self->[0], $key, $oval, $nval);
363}
364
365
366sub remove {
367    my ($self, $key) = @_;
368    return db_remove($self->[0], $key);
369}
370
371
372sub get {
373    my ($self, $key) = @_;
374    return db_get($self->[0], $key);
375}
376
377
378sub check {
379    my ($self, $key) = @_;
380    return db_check($self->[0], $key);
381}
382
383
384sub seize {
385    my ($self, $key) = @_;
386    return db_seize($self->[0], $key);
387}
388
389
390sub set_bulk {
391    my ($self, $recs) = @_;
392    return -1 if (ref($recs) ne 'HASH');
393    my $rv = 0;
394    while (my ($key, $value) = each(%$recs)) {
395        return -1 if (!db_set($self->[0], $key, $value));
396        $rv++;
397    }
398    return $rv;
399}
400
401
402sub remove_bulk {
403    my ($self, $keys) = @_;
404    return -1 if (ref($keys) ne 'ARRAY');
405    my $rv = 0;
406    foreach my $key (@$keys) {
407        $rv++ if (db_remove($self->[0], $key));
408    }
409    return $rv;
410}
411
412
413sub get_bulk {
414    my ($self, $keys) = @_;
415    return -1 if (ref($keys) ne 'ARRAY');
416    my %recs;
417    foreach my $key (@$keys) {
418        my $value = db_get($self->[0], $key);
419        $recs{$key} = $value if (defined($value));
420    }
421    return \%recs;
422}
423
424
425sub clear {
426    my ($self) = @_;
427    return db_clear($self->[0]);
428}
429
430
431sub synchronize {
432    my ($self, $hard, $proc) = @_;
433    return db_synchronize($self->[0], $hard, $proc);
434}
435
436
437sub occupy {
438    my ($self, $writable, $proc) = @_;
439    return db_occupy($self->[0], $writable, $proc);
440}
441
442
443sub copy {
444    my ($self, $dest) = @_;
445    return db_copy($self->[0], $dest);
446}
447
448
449sub begin_transaction {
450    my ($self, $hard) = @_;
451    $hard = 0 if (!defined($hard));
452    return db_begin_transaction($self->[0], $hard);
453}
454
455
456sub end_transaction {
457    my ($self, $commit) = @_;
458    $commit = 1 if (!defined($commit));
459    return db_end_transaction($self->[0], $commit);
460}
461
462
463sub transaction {
464    my ($self, $proc, $hard) = @_;
465    return 0 if (!$self->begin_transaction($hard));
466    my $commit = 0;
467    eval {
468        $commit = &$proc($self);
469    };
470    return 0 if (!$self->end_transaction($commit));
471    return 1;
472}
473
474
475sub dump_snapshot {
476    my ($self, $dest) = @_;
477    return db_dump_snapshot($self->[0], $dest);
478}
479
480
481sub load_snapshot {
482    my ($self, $src) = @_;
483    return db_load_snapshot($self->[0], $src);
484}
485
486
487sub count {
488    my ($self) = @_;
489    return db_count($self->[0]);
490}
491
492
493sub size {
494    my ($self) = @_;
495    return db_size($self->[0]);
496}
497
498
499sub path {
500    my ($self) = @_;
501    return db_path($self->[0]);
502}
503
504
505sub status {
506    my ($self) = @_;
507    my $ststr = db_status($self->[0]);
508    return undef if (!defined($ststr));
509    my %stmap;
510    my @lines = split(/\n/, $ststr);
511    foreach my $line (@lines) {
512        my @fields = split(/\t/, $line);
513        $stmap{$fields[0]} = $fields[1] if (scalar(@fields) > 1);
514    }
515    return \%stmap;
516}
517
518
519sub match_prefix {
520    my ($self, $prefix, $max) = @_;
521    $max = -1 if (!defined($max));
522    return db_match_prefix($self->[0], $prefix, $max);
523}
524
525
526sub match_regex {
527    my ($self, $regex, $max) = @_;
528    $max = -1 if (!defined($max));
529    return db_match_regex($self->[0], $regex, $max);
530}
531
532
533sub match_similar {
534    my ($self, $origin, $range, $utf, $max) = @_;
535    $range = 1 if (!defined($range));
536    $utf = 0 if (!defined($utf));
537    $max = -1 if (!defined($max));
538    return db_match_similar($self->[0], $origin, $range, $utf, $max);
539}
540
541
542sub merge {
543    my ($self, $srcary, $mode) = @_;
544    $mode = MSET() if (!defined($mode));
545    return db_merge($self->[0], $srcary, $mode);
546}
547
548
549sub cursor {
550    my ($self) = @_;
551    return KyotoCabinet::Cursor->new($self);
552}
553
554
555sub cursor_process {
556    my ($self, $proc) = @_;
557    my $cur = $self->cursor;
558    eval {
559        &$proc($cur);
560    };
561    $cur->disable;
562    return undef;
563}
564
565
566sub string {
567    my ($self) = @_;
568    my $path = $self->path;
569    $path = "(undef)" if (!defined($path));
570    return sprintf("%s: %ld: %ld", $path, $self->count, $self->size);
571}
572
573
574sub process {
575    my ($cls, $proc, $path, $mode) = @_;
576    my $db = $cls->new;
577    return $db->error if (!$db->open($path, $mode));
578    &$proc($db);
579    return $db->error if (!$db->close);
580    return undef;
581}
582
583
584sub TIEHASH {
585    my ($cls, $path, $mode) = @_;
586    my $db = $cls->new;
587    return undef if (!$db->open($path, $mode));
588    my $cur = $db->cursor;
589    undef($cur->[1]);
590    $db->[1] = $cur;
591    return $db;
592}
593
594
595sub UNTIE {
596    my ($self) = @_;
597    return $self->close;
598}
599
600
601sub FETCH {
602    return db_get($_[0]->[0], $_[1]);
603}
604
605
606sub STORE {
607    return db_set($_[0]->[0], $_[1], $_[2]);
608}
609
610
611sub DELETE {
612    return db_remove($_[0]->[0], $_[1]);
613}
614
615
616sub CLEAR {
617    return db_clear($_[0]->[0]);
618}
619
620
621sub EXISTS {
622    return defined(db_get($_[0]->[0], $_[1]));
623}
624
625
626sub FIRSTKEY {
627    my $cur = $_[0]->[1];
628    $cur->jump;
629    my $key = $cur->get_key(1);
630    $_[0]->[2] = $key;
631    return $key;
632}
633
634
635sub NEXTKEY {
636    my $cur = $_[0]->[1];
637    my $key = $cur->get_key(1);
638    return undef if (!defined($key));
639    if ($key eq $_[0]->[2]) {
640        undef($_[0]->[2]);
641        return undef;
642    }
643    return $key;
644}
645
646
647
6481;
649
650# END OF FILE
651