Commit | Line | Data |
065b45be |
1 | package DBM::Deep::Iterator; |
2 | |
9c7d9738 |
3 | use 5.006_000; |
065b45be |
4 | |
5 | use strict; |
6 | use warnings FATAL => 'all'; |
7 | |
8 | use Scalar::Util (); |
9 | |
10 | use DBM::Deep::Iterator::Index; |
11 | use DBM::Deep::Iterator::BucketList; |
12 | |
13 | sub new { |
14 | my $class = shift; |
15 | my ($args) = @_; |
16 | |
17 | my $self = bless { |
18 | breadcrumbs => [], |
19 | engine => $args->{engine}, |
20 | base_offset => $args->{base_offset}, |
21 | }, $class; |
22 | |
23 | Scalar::Util::weaken( $self->{engine} ); |
24 | |
25 | return $self; |
26 | } |
27 | |
28 | sub reset { $_[0]{breadcrumbs} = [] } |
29 | |
30 | sub get_sector_iterator { |
31 | my $self = shift; |
32 | my ($loc) = @_; |
33 | |
34 | my $sector = $self->{engine}->_load_sector( $loc ) |
35 | or return; |
36 | |
37 | if ( $sector->isa( 'DBM::Deep::Engine::Sector::Index' ) ) { |
38 | return DBM::Deep::Iterator::Index->new({ |
39 | iterator => $self, |
40 | sector => $sector, |
41 | }); |
42 | } |
43 | elsif ( $sector->isa( 'DBM::Deep::Engine::Sector::BucketList' ) ) { |
44 | return DBM::Deep::Iterator::BucketList->new({ |
45 | iterator => $self, |
46 | sector => $sector, |
47 | }); |
48 | } |
49 | |
50 | DBM::Deep->_throw_error( "get_sector_iterator(): Why did $loc make a $sector?" ); |
51 | } |
52 | |
53 | sub get_next_key { |
54 | my $self = shift; |
55 | my ($obj) = @_; |
56 | |
57 | my $crumbs = $self->{breadcrumbs}; |
58 | my $e = $self->{engine}; |
59 | |
60 | unless ( @$crumbs ) { |
61 | # This will be a Reference sector |
62 | my $sector = $e->_load_sector( $self->{base_offset} ) |
63 | # If no sector is found, thist must have been deleted from under us. |
64 | or return; |
65 | |
66 | if ( $sector->staleness != $obj->_staleness ) { |
67 | return; |
68 | } |
69 | |
70 | my $loc = $sector->get_blist_loc |
71 | or return; |
72 | |
73 | push @$crumbs, $self->get_sector_iterator( $loc ); |
74 | } |
75 | |
76 | FIND_NEXT_KEY: { |
77 | # We're at the end. |
78 | unless ( @$crumbs ) { |
79 | $self->reset; |
80 | return; |
81 | } |
82 | |
83 | my $iterator = $crumbs->[-1]; |
84 | |
85 | # This level is done. |
86 | if ( $iterator->at_end ) { |
87 | pop @$crumbs; |
88 | redo FIND_NEXT_KEY; |
89 | } |
90 | |
91 | if ( $iterator->isa( 'DBM::Deep::Iterator::Index' ) ) { |
92 | # If we don't have any more, it will be caught at the |
93 | # prior check. |
94 | if ( my $next = $iterator->get_next_iterator ) { |
95 | push @$crumbs, $next; |
96 | } |
97 | redo FIND_NEXT_KEY; |
98 | } |
99 | |
100 | unless ( $iterator->isa( 'DBM::Deep::Iterator::BucketList' ) ) { |
101 | DBM::Deep->_throw_error( |
102 | "Should have a bucketlist iterator here - instead have $iterator" |
103 | ); |
104 | } |
105 | |
106 | # At this point, we have a BucketList iterator |
107 | my $key = $iterator->get_next_key; |
108 | if ( defined $key ) { |
109 | return $key; |
110 | } |
111 | #XXX else { $iterator->set_to_end() } ? |
112 | |
113 | # We hit the end of the bucketlist iterator, so redo |
114 | redo FIND_NEXT_KEY; |
115 | } |
116 | |
117 | DBM::Deep->_throw_error( "get_next_key(): How did we get here?" ); |
118 | } |
119 | |
120 | 1; |
121 | __END__ |