Created concept of Storage:: in order to start adding more storage backends
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / Iterator.pm
1 package DBM::Deep::Iterator;
2
3 use 5.006_000;
4
5 use strict;
6 use warnings FATAL => 'all';
7
8 use DBM::Deep::Iterator::BucketList ();
9 use DBM::Deep::Iterator::Index ();
10
11 =head1 NAME
12
13 DBM::Deep::Iterator
14
15 =head1 PURPOSE
16
17 This is an internal-use-only object for L<DBM::Deep/>. It is the iterator
18 for FIRSTKEY() and NEXTKEY().
19
20 =head1 OVERVIEW
21
22 This object 
23
24 =head1 METHODS
25
26 =head2 new(\%params)
27
28 The constructor takes a hashref of params. The hashref is assumed to have the
29 following elements:
30
31 =over 4
32
33 =item * engine (of type L<DBM::Deep::Engine/>
34
35 =item * base_offset (the base_offset of the invoking DBM::Deep object)
36
37 =back
38
39 =cut
40
41 sub new {
42     my $class = shift;
43     my ($args) = @_;
44
45     my $self = bless {
46         breadcrumbs => [],
47         engine      => $args->{engine},
48         base_offset => $args->{base_offset},
49     }, $class;
50
51     Scalar::Util::weaken( $self->{engine} );
52
53     return $self;
54 }
55
56 =head2 reset()
57
58 This method takes no arguments.
59
60 It will reset the iterator so that it will start from the beginning again.
61
62 This method returns nothing.
63
64 =cut
65
66 sub reset { $_[0]{breadcrumbs} = [] }
67
68 =head2 get_sector_iterator( $loc )
69
70 This takes a location. It will load the sector for $loc, then instantiate the right
71 iteartor type for it.
72
73 This returns the sector iterator.
74
75 =cut
76
77 sub get_sector_iterator {
78     my $self = shift;
79     my ($loc) = @_;
80
81     my $sector = $self->{engine}->_load_sector( $loc )
82         or return;
83
84     if ( $sector->isa( 'DBM::Deep::Engine::Sector::Index' ) ) {
85         return DBM::Deep::Iterator::Index->new({
86             iterator => $self,
87             sector   => $sector,
88         });
89     }
90     elsif ( $sector->isa( 'DBM::Deep::Engine::Sector::BucketList' ) ) {
91         return DBM::Deep::Iterator::BucketList->new({
92             iterator => $self,
93             sector   => $sector,
94         });
95     }
96
97     DBM::Deep->_throw_error( "get_sector_iterator(): Why did $loc make a $sector?" );
98 }
99
100 =head2 get_next_key( $obj )
101
102 =cut
103
104 sub get_next_key {
105     my $self = shift;
106     my ($obj) = @_;
107
108     my $crumbs = $self->{breadcrumbs};
109     my $e = $self->{engine};
110
111     unless ( @$crumbs ) {
112         # This will be a Reference sector
113         my $sector = $e->_load_sector( $self->{base_offset} )
114             # If no sector is found, this must have been deleted from under us.
115             or return;
116
117         if ( $sector->staleness != $obj->_staleness ) {
118             return;
119         }
120
121         my $loc = $sector->get_blist_loc
122             or return;
123
124         push @$crumbs, $self->get_sector_iterator( $loc );
125     }
126
127     FIND_NEXT_KEY: {
128         # We're at the end.
129         unless ( @$crumbs ) {
130             $self->reset;
131             return;
132         }
133
134         my $iterator = $crumbs->[-1];
135
136         # This level is done.
137         if ( $iterator->at_end ) {
138             pop @$crumbs;
139             redo FIND_NEXT_KEY;
140         }
141
142         if ( $iterator->isa( 'DBM::Deep::Iterator::Index' ) ) {
143             # If we don't have any more, it will be caught at the
144             # prior check.
145             if ( my $next = $iterator->get_next_iterator ) {
146                 push @$crumbs, $next;
147             }
148             redo FIND_NEXT_KEY;
149         }
150
151         unless ( $iterator->isa( 'DBM::Deep::Iterator::BucketList' ) ) {
152             DBM::Deep->_throw_error(
153                 "Should have a bucketlist iterator here - instead have $iterator"
154             );
155         }
156
157         # At this point, we have a BucketList iterator
158         my $key = $iterator->get_next_key;
159         if ( defined $key ) {
160             return $key;
161         }
162         #XXX else { $iterator->set_to_end() } ?
163
164         # We hit the end of the bucketlist iterator, so redo
165         redo FIND_NEXT_KEY;
166     }
167
168     DBM::Deep->_throw_error( "get_next_key(): How did we get here?" );
169 }
170
171 1;
172 __END__