File Coverage

blib/lib/Net/IPAM/Tree/Private.pm
Criterion Covered Total %
statement 75 75 100.0
branch 24 24 100.0
condition 6 6 100.0
subroutine 11 11 100.0
pod n/a
total 116 116 100.0


line stmt bran cond sub pod time code
1             package Net::IPAM::Tree::Private;
2              
3 6     6   72 use 5.10.0;
  6         22  
4 6     6   32 use strict;
  6         11  
  6         111  
5 6     6   29 use warnings;
  6         10  
  6         550  
6 6     6   82 use utf8;
  6         24  
  6         43  
7 6     6   3851 use List::MoreUtils qw();
  6         84882  
  6         5200  
8              
9             =head1 NAME
10              
11             Net::IPAM::Tree::Private - private implementation for Net::IPAM::Tree
12              
13             =head1 SYNOPSIS
14              
15             This module is not useful standalone, it's just needed for Net::IPAM::Tree.
16             The implementation details are hidden by the public API in Net::IPAM::Tree.
17              
18             =head1 FUNCTIONS
19              
20             =head2 _buildIndexTree($tree, $parent, $child)
21              
22             Building the tree with just the array indices, the sorted items itself are not moved.
23             create the {parent}->[childs] map, rec-descent algo.
24              
25             =cut
26              
27             sub _build_index_tree {
28 52     52   102 my ( $t, $parent, $child ) = @_;
29              
30             # if parent has no childs yet, just append the child idx
31 52 100       119 if ( not defined $t->{_tree}{$parent} ) {
32 14         22 push @{ $t->{_tree}{$parent} }, $child;
  14         42  
33 14         33 return;
34             }
35              
36             # everything is sorted, just look for previous child for coverage
37              
38             # get prev child idx for this parent
39 38         59 my $prev = $t->{_tree}{$parent}[-1];
40              
41             # item is covered by previous child, it's an ancestot, not a sibling
42 38 100       92 if ( $t->{_items}[$prev]->contains( $t->{_items}[$child] ) ) {
43              
44             # rec-descent
45 16         466 _build_index_tree( $t, $prev, $child );
46 16         34 return;
47             }
48              
49             # not covered by previous child, append as sibling
50 22         495 push @{ $t->{_tree}{$parent} }, $child;
  22         51  
51              
52 22         51 return;
53             }
54              
55             ####
56             # _superset($block)
57             #
58             # returns the outermost containing block or undef
59             sub _superset {
60 8     8   14 my ( $t, $thing ) = @_;
61              
62             # find first item in root-level equal to or superset of block
63 8         14 for my $i ( @{ $t->{_tree}{_ROOT} } ) {
  8         22  
64 23 100 100     522 if ( $t->{_items}[$i]->cmp($thing) == 0 || $t->{_items}[$i]->contains($thing) ) {
65 3         103 return $t->{_items}[$i];
66             }
67             }
68              
69 5         99 return;
70             }
71              
72             ####
73             # _lookup($block)
74             #
75             # Returns item in tree with longest-prefix-match for $block, returns undef if not found.
76             #
77             # thing is a Net::IPAM::Block or a subclass of them
78             #
79             # returns the lpm block
80             #
81             # rec-descent algo
82             sub _lookup {
83 14     14   30 my ( $t, $parent, $block ) = @_;
84              
85             # derefernce child idxs array
86 14         30 my $c_idxs = $t->{_tree}{$parent};
87              
88             # find first index where child->{block} >= block
89             my $idx =
90 14     25   87 List::MoreUtils::lower_bound { $t->{_items}[$_]->cmp($block) } @$c_idxs;
  25         167  
91              
92             # found by exact match?
93             # search index may be -1 or at end, take care for index panics
94 14 100 100     159 if ( $idx >= 0 and $idx < @$c_idxs ) {
95              
96             # deref for better reading and debugging
97 7         14 my $i = $c_idxs->[$idx];
98              
99 7 100       18 if ( $t->{_items}[$i]->cmp($block) == 0 ) {
100 2         33 return $t->{_items}[$i];
101             }
102             }
103              
104             # look if child before idx contains block
105             # search index may be 0, take care for index panics
106 12 100       65 if ( $idx > 0 ) {
107              
108             # deref for better reading and debugging
109 7         14 my $i = $c_idxs->[ $idx - 1 ];
110              
111 7 100       20 if ( $t->{_items}[$i]->contains($block) ) {
112              
113             # rec-descent
114 3         84 return _lookup( $t, $i, $block );
115             }
116             }
117              
118             # return parent at this level, if root returns undef
119 9 100       107 if ( $parent eq '_ROOT' ) {
120 7         36 return;
121             }
122              
123 2         10 return $t->{_items}[$parent];
124             }
125              
126             # recdescent to string
127             sub _to_string {
128 24     24   67 my ( $t, $cb, $parent, $buf, $prefix ) = @_;
129              
130 24         45 my $c_idxs = $t->{_tree}{$parent};
131              
132             # STOP condition, no more childs
133 24 100       72 unless ( defined $c_idxs ) {
134 17         50 return $buf;
135             }
136              
137 7         13 my $len_c = @$c_idxs;
138              
139             # stop before last child
140 7         17 for my $i ( @{$c_idxs}[ 0 .. $len_c - 2 ] ) {
  7         18  
141 13         38 $buf .= $prefix . "├─ " . $cb->( $t->{_items}[$i] ) . "\n";
142 13         821 $buf = _to_string( $t, $cb, $i, $buf, $prefix . "│ " );
143             }
144              
145             # last child
146 7         12 my $i = $c_idxs->[-1];
147 7         22 $buf .= $prefix . "└─ " . $cb->( $t->{_items}[$i] ) . "\n";
148 7         321 $buf = _to_string( $t, $cb, $i, $buf, $prefix . " " );
149              
150 7         18 return $buf;
151             }
152              
153             # walk the tree, call the cb for every item with:
154             # my $err = $cb->(
155             # {
156             # depth => $depth,
157             # item => $item,
158             # parent => $parent,
159             # childs => [@childs],
160             # }
161             # );
162             #
163             sub _walk {
164 13     13   26 my ( $t, $cb, $depth, $p, $i ) = @_;
165              
166 13         19 my $parent;
167 13 100       23 if ( defined $p ) {
168 10         14 $parent = $t->{_items}[$p];
169             }
170              
171 13         22 my $item = $t->{_items}[$i];
172 13         23 my $c_idxs = $t->{_tree}{$i};
173              
174 13         19 my @childs;
175 13         18 foreach my $c (@$c_idxs) {
176 10         20 push @childs, $t->{_items}[$c];
177             }
178              
179 13         49 my $err = $cb->(
180             {
181             depth => $depth,
182             item => $item,
183             parent => $parent,
184             childs => [@childs],
185             }
186             );
187              
188 13 100       988 return $err if $err;
189              
190 12         20 foreach my $c (@$c_idxs) {
191 10         35 my $err = _walk( $t, $cb, $depth + 1, $i, $c );
192 10 100       23 return $err if $err;
193             }
194              
195 10         22 return;
196             }
197              
198             =head1 AUTHOR
199              
200             Karl Gaissmaier, C<< >>
201              
202             =head1 SUPPORT
203              
204             You can find documentation for this module with the perldoc command.
205              
206             perldoc Net::IPAM::Tree::Private
207              
208             You can also look for information at:
209              
210             =over 4
211              
212             =item * on github
213              
214             TODO
215              
216             =back
217              
218             =head1 LICENSE AND COPYRIGHT
219              
220             This software is copyright (c) 2020-2021 by Karl Gaissmaier.
221              
222             This is free software; you can redistribute it and/or modify it under
223             the same terms as the Perl 5 programming language system itself.
224              
225             =encoding utf8
226              
227              
228             =cut
229              
230             1;