Commit | Line | Data |
f0276afb |
1 | package DBM::Deep::Iterator; |
2 | |
3 | use 5.006_000; |
4 | |
5 | use strict; |
6 | use warnings FATAL => 'all'; |
7 | |
5ae752e2 |
8 | use DBM::Deep::Iterator::BucketList (); |
9 | use DBM::Deep::Iterator::Index (); |
10 | |
f0276afb |
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 | |
d6ecf579 |
81 | my $sector = $self->{engine}->load_sector( $loc ) |
f0276afb |
82 | or return; |
83 | |
2c70efe1 |
84 | if ( $sector->isa( 'DBM::Deep::Sector::File::Index' ) ) { |
f0276afb |
85 | return DBM::Deep::Iterator::Index->new({ |
86 | iterator => $self, |
87 | sector => $sector, |
88 | }); |
89 | } |
2c70efe1 |
90 | elsif ( $sector->isa( 'DBM::Deep::Sector::File::BucketList' ) ) { |
f0276afb |
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 |
d6ecf579 |
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 | |
171 | 1; |
172 | __END__ |