File Coverage

lib/Net/BART/Node.pm
Criterion Covered Total %
statement 63 73 86.3
branch 14 24 58.3
condition 1 3 33.3
subroutine 18 20 90.0
pod n/a
total 96 120 80.0


line stmt bran cond sub pod time code
1             package Net::BART::Node;
2              
3 1     1   8 use strict;
  1         3  
  1         48  
4 1     1   6 use warnings;
  1         2  
  1         68  
5 1     1   7 use Net::BART::SparseArray256;
  1         2  
  1         58  
6              
7             our $VERSION = '0.01';
8              
9 1     1   6 use Net::BART::Art qw(pfx_to_idx octet_to_idx idx_to_pfx pfx_bits prefix_decompose);
  1         2  
  1         114  
10              
11             # --- LeafNode: path-compressed leaf storing a full prefix + value ---
12             # Structure: bless [$addr, $prefix_len, $value], 'Net::BART::Node::Leaf'
13             # Constants for field access:
14 1     1   8 use constant { LEAF_ADDR => 0, LEAF_PFXLEN => 1, LEAF_VALUE => 2 };
  1         2  
  1         753  
15              
16             package Net::BART::Node::Leaf {
17             sub new {
18 10     10   45 my ($class, %args) = @_;
19 10         48 return bless [$args{addr}, $args{prefix_len}, $args{value}], $class;
20             }
21              
22             sub contains_ip {
23 5     5   10 my ($self, $ip_bytes) = @_;
24 5         16 my $pfx = $self->[0]; # addr
25 5         6 my $pfx_len = $self->[1];
26 5         11 my $full_bytes = $pfx_len >> 3; # int($pfx_len / 8)
27 5         11 for my $i (0 .. $full_bytes - 1) {
28 24 100       55 return 0 if $pfx->[$i] != $ip_bytes->[$i];
29             }
30 3         7 my $remaining = $pfx_len & 7;
31 3 50       9 if ($remaining) {
32 0         0 my $mask = (0xFF << (8 - $remaining)) & 0xFF;
33 0 0       0 return 0 if ($pfx->[$full_bytes] & $mask) != ($ip_bytes->[$full_bytes] & $mask);
34             }
35 3         12 return 1;
36             }
37              
38             sub matches_prefix {
39 6     6   12 my ($self, $addr, $prefix_len) = @_;
40 6 100       22 return 0 if $self->[1] != $prefix_len;
41 3         6 my $pfx = $self->[0];
42 3         6 my $full_bytes = $prefix_len >> 3;
43 3         9 for my $i (0 .. $full_bytes - 1) {
44 7 100       22 return 0 if $pfx->[$i] != $addr->[$i];
45             }
46 1         2 my $remaining = $prefix_len & 7;
47 1 50       4 if ($remaining) {
48 1         3 my $mask = (0xFF << (8 - $remaining)) & 0xFF;
49 1 50       6 return 0 if ($pfx->[$full_bytes] & $mask) != ($addr->[$full_bytes] & $mask);
50             }
51 0         0 return 1;
52             }
53             }
54              
55             # --- FringeNode: stride-aligned prefix, value only ---
56             # Structure: bless [$value], 'Net::BART::Node::Fringe'
57              
58             package Net::BART::Node::Fringe {
59             sub new {
60 270     270   926 my ($class, %args) = @_;
61 270         1449 return bless [$args{value}], $class;
62             }
63             }
64              
65             # --- BartNode: internal trie node ---
66             # Structure: bless [$prefixes_sparse_array, $children_sparse_array], 'Net::BART::Node::Bart'
67 1     1   9 use constant { BART_PFX => 0, BART_CHD => 1 };
  1         12  
  1         859  
68              
69             package Net::BART::Node::Bart {
70             sub new {
71 37     37   133 return bless [Net::BART::SparseArray256->new, Net::BART::SparseArray256->new], $_[0];
72             }
73              
74             # Prefix operations - delegate to sparse array
75 8     8   23 sub insert_prefix { return $_[0]->[0]->insert_at($_[1], $_[2]) }
76 0     0   0 sub delete_prefix { return $_[0]->[0]->delete_at($_[1]) }
77 1     1   3 sub get_prefix { return $_[0]->[0]->get($_[1]) }
78              
79             # Child operations
80 811     811   2077 sub get_child { return $_[0]->[1]->get($_[1]) }
81 291     291   747 sub set_child { return $_[0]->[1]->insert_at($_[1], $_[2]) }
82 1     1   4 sub delete_child { return $_[0]->[1]->delete_at($_[1]) }
83              
84             # Longest-prefix-match at this node for the given octet.
85             # Returns (value, 1) if found, (undef, 0) if not.
86             sub lpm {
87 16     16   24 my $self = $_[0];
88 16         29 my $idx = ($_[1] >> 1) + 128; # octet_to_idx inlined
89 16         26 my $pfx_bs = $self->[0][0]; # prefixes sparse array -> bitset
90 16         31 my $lut = $Net::BART::LPM::LOOKUP_TBL[$idx];
91              
92             # intersection_top inlined
93 16         24 my $w;
94 16 50       32 $w = $pfx_bs->[3] & $lut->[3]; if ($w) { return $self->[0]->get(192 + Net::BART::BitSet256::_bit_len64($w) - 1) }
  16         33  
  0         0  
95 16 50       23 $w = $pfx_bs->[2] & $lut->[2]; if ($w) { return $self->[0]->get(128 + Net::BART::BitSet256::_bit_len64($w) - 1) }
  16         31  
  0         0  
96 16 50       31 $w = $pfx_bs->[1] & $lut->[1]; if ($w) { return $self->[0]->get( 64 + Net::BART::BitSet256::_bit_len64($w) - 1) }
  16         33  
  0         0  
97 16 100       26 $w = $pfx_bs->[0] & $lut->[0]; if ($w) { return $self->[0]->get( Net::BART::BitSet256::_bit_len64($w) - 1) }
  16         56  
  8         30  
98 8         23 return (undef, 0);
99             }
100              
101             # Check if any prefix at this node matches the given octet.
102             sub lpm_test {
103 0     0   0 my $pfx_bs = $_[0]->[0][0]; # prefixes -> bitset
104 0         0 my $lut = $Net::BART::LPM::LOOKUP_TBL[($_[1] >> 1) + 128];
105 0 0       0 return (($pfx_bs->[0] & $lut->[0]) |
106             ($pfx_bs->[1] & $lut->[1]) |
107             ($pfx_bs->[2] & $lut->[2]) |
108             ($pfx_bs->[3] & $lut->[3])) ? 1 : 0;
109             }
110              
111             sub is_empty {
112 1   33 1   1 return !@{$_[0]->[0][1]} && !@{$_[0]->[1][1]};
113             }
114             }
115              
116             1;