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