Created concept of Storage:: in order to start adding more storage backends
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / Iterator.pm
CommitLineData
f0276afb 1package DBM::Deep::Iterator;
2
3use 5.006_000;
4
5use strict;
6use warnings FATAL => 'all';
7
5ae752e2 8use DBM::Deep::Iterator::BucketList ();
9use DBM::Deep::Iterator::Index ();
10
f0276afb 11=head1 NAME
12
13DBM::Deep::Iterator
14
15=head1 PURPOSE
16
17This is an internal-use-only object for L<DBM::Deep/>. It is the iterator
18for FIRSTKEY() and NEXTKEY().
19
20=head1 OVERVIEW
21
22This object
23
24=head1 METHODS
25
26=head2 new(\%params)
27
28The constructor takes a hashref of params. The hashref is assumed to have the
29following 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
41sub 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
58This method takes no arguments.
59
60It will reset the iterator so that it will start from the beginning again.
61
62This method returns nothing.
63
64=cut
65
66sub reset { $_[0]{breadcrumbs} = [] }
67
68=head2 get_sector_iterator( $loc )
69
70This takes a location. It will load the sector for $loc, then instantiate the right
71iteartor type for it.
72
73This returns the sector iterator.
74
75=cut
76
77sub 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
104sub 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} )
d426259c 114 # If no sector is found, this must have been deleted from under us.
f0276afb 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
1711;
172__END__