File Coverage

blib/lib/Net/IPAM/Block/Private.pm
Criterion Covered Total %
statement 129 129 100.0
branch 29 32 90.6
condition 20 22 90.9
subroutine 24 24 100.0
pod n/a
total 202 207 97.5


line stmt bran cond sub pod time code
1             package Net::IPAM::Block::Private;
2              
3 13     13   133 use 5.10.0;
  13         34  
4 13     13   59 use strict;
  13         18  
  13         244  
5 13     13   60 use warnings;
  13         23  
  13         245  
6 13     13   51 use utf8;
  13         24  
  13         57  
7              
8 13     13   285 use Carp ();
  13         21  
  13         255  
9 13     13   54 use Scalar::Util ();
  13         18  
  13         211  
10 13     13   66 use Net::IPAM::IP ();
  13         47  
  13         27589  
11              
12             =head1 NAME
13              
14             Net::IPAM::Block::Private - Just private functions for Net::IPAM::Block
15              
16             =head1 DESCRIPTION
17              
18             Don't pollute the B<< namespace >> of Net::IPAM::Block
19              
20             =cut
21              
22             # use some pre-calculated tables for speed, see end of package
23             my ( %valid_masks4, @table_mask_n, @nlz8 );
24              
25             # 1.2.3.4/255.0.0.0 => {base: 1.0.0.0, last: 1.255.255.255, mask: 255.0.0.0}
26             # 1.2.3.4/24 => {base: 1.2.3.0, last: 1.2.3.255, mask: 255.255.255.0}
27             # fe80::1/124 => {base: fe80::, last: fe80::f, mask: ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0}
28             sub _fromMask {
29 227     227   346 my ( $self, $addr, $idx ) = @_;
30              
31             # split input in prefix and suffix:
32             # 10.0.0.0/255.0.0.0 => 10.0.0.0, 255.0.0.0
33             # 10.0.0.0/8 => 10.0.0.0, 8
34             # fe80::1/124 => fe80::1, 124
35             #
36 227         369 my $prefix_str = substr( $addr, 0, $idx );
37 227         333 my $suffix_str = substr( $addr, $idx + 1 );
38              
39 227   100     477 my $prefix = Net::IPAM::IP->new($prefix_str) // return;
40              
41 225         3519 my $mask_n;
42 225 100       416 if ( index( $suffix_str, '.' ) >= 0 ) {
43 7   100     13 my $ip = Net::IPAM::IP->new($suffix_str) // return;
44 6         79 $mask_n = $ip->bytes;
45 6 100       32 return unless exists $valid_masks4{$mask_n};
46             }
47             else {
48 218         248 my $bits = 32;
49 218 100       369 $bits = 128 if $prefix->version == 6;
50              
51 218 100       1223 return unless $suffix_str =~ m/^\d+$/; # pos integer
52 216 100       437 return if $suffix_str > $bits;
53              
54 212         355 $mask_n = _make_mask_n( $suffix_str, $bits );
55             }
56              
57 217         446 $self->{base} = Net::IPAM::IP->new_from_bytes( _make_base_n( $prefix->bytes, $mask_n ) );
58 217         2798 $self->{last} = Net::IPAM::IP->new_from_bytes( _make_last_n( $prefix->bytes, $mask_n ) );
59              
60 217         2619 return $self;
61             }
62              
63             # 1.2.3.4-1.2.3.17 => {base: 1.2.3.4, last: 1.2.3.17, mask: undef}
64             # fe80::-fe80::ffff => {base: fe80::, last: fe80::ffff, mask: ffff:ffff:ffff:ffff:ffff:ffff:ffff::}
65             sub _fromRange {
66 81     81   130 my ( $self, $addr, $idx ) = @_;
67              
68             # split range in base and last 10.0.0.1-10.0.0.3 => 10.0.0.1, 10.0.0.3
69 81         146 my $base_str = substr( $addr, 0, $idx );
70 81         127 my $last_str = substr( $addr, $idx + 1 );
71              
72 81   100     186 my $base_ip = Net::IPAM::IP->new($base_str) // return;
73 80   100     1398 my $last_ip = Net::IPAM::IP->new($last_str) // return;
74              
75             # version base != version last
76 78         1052 my $version = $base_ip->version;
77 78 100       246 return if $version != $last_ip->version;
78              
79             # base > last?
80 76 100       274 return if $base_ip->cmp($last_ip) > 0;
81              
82 74         350 $self->{base} = $base_ip;
83 74         112 $self->{last} = $last_ip;
84              
85 74         228 return $self;
86             }
87              
88             # 1.2.3.4 => {base: 1.2.3.4, last: 1.2.3.4, mask: 255.255.255.255}
89             # fe80::1 => {base: fe80::1, last: fe80::1, mask: ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff}
90             sub _fromAddr {
91 65     65   95 my ( $self, $addr ) = @_;
92              
93 65   100     130 my $base = Net::IPAM::IP->new($addr) // return;
94              
95 63         1026 $self->{base} = $base;
96 63         79 $self->{last} = $base;
97              
98 63         141 return $self;
99             }
100              
101             # 1.2.3.4 => {base: 1.2.3.4, last: 1.2.3.4, mask: 255.255.255.255}
102             # fe80::1 => {base: fe80::1, last: fe80::1, mask: ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff}
103             sub _fromIP {
104 4     4   8 my ( $self, $ip ) = @_;
105              
106 4 50 33     57 return unless Scalar::Util::blessed($ip) && $ip->isa('Net::IPAM::IP');
107              
108 4         12 $self->{base} = $ip;
109 4         7 $self->{last} = $ip;
110              
111 4         7 return $self;
112             }
113              
114             # my $b = _clone($self)
115             sub _clone {
116 19     19   26 my $self = shift;
117 19         34 my $clone = bless {}, ref $self;
118              
119 19         40 $clone->{base} = Net::IPAM::IP->new_from_bytes( $self->{base}->bytes );
120 19         249 $clone->{last} = Net::IPAM::IP->new_from_bytes( $self->{last}->bytes );
121              
122 19         215 return $clone;
123             }
124              
125             # a.base <= b && a.last >= b
126             sub _contains_ip {
127 8   100 8   17 return $_[0]->{base}->cmp( $_[1] ) <= 0 && $_[0]->{last}->cmp( $_[1] ) >= 0;
128             }
129              
130             sub _contains_block {
131              
132             # a == b, return false
133 249 100   249   377 return 0 if $_[0]->cmp( $_[1] ) == 0;
134              
135             # a.base <= b.base && a.last >= b.last
136 247   100     1089 return $_[0]->{base}->cmp( $_[1]->{base} ) <= 0 && $_[0]->{last}->cmp( $_[1]->{last} ) >= 0;
137             }
138              
139             # count leading zeros
140             sub _leading_zeros {
141 678 50   678   1079 my $n = shift or die 'missing arg,';
142 678         726 my $zeros = 0;
143              
144             # step byte-wise through $n from left to right
145 678         1132 for my $i ( 0 .. length($n) - 1 ) {
146 4359         4842 my $byte = vec( $n, $i, 8 );
147              
148             # count bytes
149 4359 100       5805 if ( $byte == 0x00 ) {
150 3789         3819 $zeros += 8;
151 3789         4283 next;
152             }
153              
154             # count bits, use a pre-calculated table
155 570         709 $zeros += $nlz8[$byte];
156 570         665 last;
157             }
158              
159 678         1321 return $zeros;
160             }
161              
162             # number of common leading bits
163             sub _common_prefix {
164 678     678   2634 my ( $base_n, $last_n ) = @_;
165 678         1176 return _leading_zeros( $base_n ^ $last_n );
166             }
167              
168             # returns the common prefix bits and ok. ok is true when the range is a CIDR.
169             sub _common_ok {
170 503     503   778 my ( $base_n, $last_n, $bits ) = @_;
171              
172 503         735 my $pfx = _common_prefix( $base_n, $last_n );
173 503         736 my $mask_n = _make_mask_n( $pfx, $bits );
174              
175             # apply mask to base and last as AND and OR
176             # and check for all zeros and all ones => it's a CIDR
177 503         903 my $is_all_zeros = ( $base_n ^ ( $base_n & $mask_n ) ) eq _make_mask_n( 0, $bits );
178 503         765 my $is_all_ones = ( $last_n | $mask_n ) eq _make_mask_n( 128, $bits );
179              
180 503   100     1512 return $pfx, $is_all_zeros && $is_all_ones;
181             }
182              
183             # switch to binary representation
184             # push start block on stack
185             #
186             # loop while stack not dry
187             # check if is_cidr
188             # split the range in the middle
189             # put both halves on stack (reverse order)
190             # next
191             #
192             # return cidrs
193             #
194             sub _to_cidrs_iter {
195 10     10   12 my $self = shift;
196 10         16 my @stack;
197             my @result;
198              
199 10         13 my $bits = 32;
200 10 100       17 $bits = 128 if $self->version == 6;
201              
202             # make binary representation, put it as start on the stack
203 10         73 push @stack, [ $self->{base}->bytes, $self->{last}->bytes ];
204              
205 10         66 while ( scalar @stack > 0 ) {
206 278         330 my $block_n = pop @stack;
207 278         349 my $base_n = $block_n->[0];
208 278         294 my $last_n = $block_n->[1];
209              
210 278         371 my ( $pfx, $is_cidr ) = _common_ok( $base_n, $last_n, $bits );
211              
212             # is this block already a CIDR?
213 278 100       416 if ($is_cidr) {
214              
215             # add block ro result
216 144         242 my $block = bless {}, ref $self;
217 144         298 $block->{base} = Net::IPAM::IP->new_from_bytes($base_n);
218 144         1792 $block->{last} = Net::IPAM::IP->new_from_bytes($last_n);
219              
220 144         1571 push @result, $block;
221 144         321 next;
222             }
223              
224             # split block in two halves, add 1 to common prefix
225 134         191 my $mask_n = _make_mask_n( $pfx + 1, $bits );
226              
227             # split range with new mask
228 134         201 my $mid_last_n = _make_last_n( $base_n, $mask_n );
229 134         193 my $mid_base_n = _make_base_n( $last_n, $mask_n );
230              
231             # make two new blocks
232 134         230 my $block1_n = [ $base_n, $mid_last_n ];
233 134         222 my $block2_n = [ $mid_base_n, $last_n ];
234              
235             # push both blocks to stack, reverse the order
236 134         313 push @stack, $block2_n, $block1_n;
237             }
238              
239 10         31 return \@result;
240             }
241              
242             # returns true if base and last forms a CIDR range
243             sub _is_cidr {
244 225     225   280 my $self = shift;
245              
246 225         253 my $bits = 32;
247 225 100       367 $bits = 128 if $self->version == 6;
248              
249 225         815 my $base_n = $self->{base}->bytes;
250 225         768 my $last_n = $self->{last}->bytes;
251              
252 225         685 my ( $pfx, $is_cidr ) = _common_ok( $base_n, $last_n, $bits );
253 225         606 return $is_cidr;
254             }
255              
256             # base = addr & netMask
257             sub _make_base_n {
258 360     360   920 my ( $addr_n, $mask_n ) = @_;
259 360         923 return $addr_n & $mask_n;
260             }
261              
262             # last = addr | hostMask
263             sub _make_last_n {
264 369     369   895 my ( $addr_n, $mask_n ) = @_;
265 369         816 return $addr_n | ~$mask_n;
266             }
267              
268             # cut off the bits after 32, used for IPv4
269 537     537   1051 sub _strip_to_32_bits { return $_[0] & "\xff\xff\xff\xff" }
270              
271             # make CIDR mask from bits:
272             # (24, 32) => 0xffff_ff00
273             # (56, 128) => 0xffff_ffff_ffff_ff00_0000_0000_0000_0000
274             #
275             sub _make_mask_n {
276 1866     1866   2388 my ( $n, $bits ) = @_;
277 1866         2182 my $mask_n = $table_mask_n[$n];
278             #
279             # cut to first 32 bits
280 1866 100       2541 if ( $bits == 32 ) {
281 537         689 return _strip_to_32_bits($mask_n);
282             }
283 1329         1780 return $mask_n;
284             }
285              
286             sub _get_mask_n {
287 2     2   3 my ( $base_ip, $last_ip ) = @_;
288              
289 2         5 my $pfx = _common_prefix( $base_ip->bytes, $last_ip->bytes );
290              
291 2         3 my $bits = 32;
292 2 50       5 $bits = 128 if $base_ip->version == 6;
293              
294 2         10 return _make_mask_n( $pfx, $bits );
295             }
296              
297             #############################
298             # pre-calc tables
299             #############################
300              
301             #<<< skip marker for perltidy
302              
303             # pre-calced masks for 0.0.0.0 - 255.255.255.255
304             # only IPv4 addresses representable as a sequence of 1 bits followed by 0 bits
305             # are allowed as masks as input
306              
307             %valid_masks4 = (
308             "\x00\x00\x00\x00" => 0, "\x80\x00\x00\x00" => 1, "\xc0\x00\x00\x00" => 2,
309             "\xe0\x00\x00\x00" => 3, "\xf0\x00\x00\x00" => 4, "\xf8\x00\x00\x00" => 5,
310             "\xfc\x00\x00\x00" => 6, "\xfe\x00\x00\x00" => 7, "\xff\x00\x00\x00" => 8,
311             "\xff\x80\x00\x00" => 9, "\xff\xc0\x00\x00" => 10, "\xff\xe0\x00\x00" => 11,
312             "\xff\xf0\x00\x00" => 12, "\xff\xf8\x00\x00" => 13, "\xff\xfc\x00\x00" => 14,
313             "\xff\xfe\x00\x00" => 15, "\xff\xff\x00\x00" => 16, "\xff\xff\x80\x00" => 17,
314             "\xff\xff\xc0\x00" => 18, "\xff\xff\xe0\x00" => 19, "\xff\xff\xf0\x00" => 20,
315             "\xff\xff\xf8\x00" => 21, "\xff\xff\xfc\x00" => 22, "\xff\xff\xfe\x00" => 23,
316             "\xff\xff\xff\x00" => 24, "\xff\xff\xff\x80" => 25, "\xff\xff\xff\xc0" => 26,
317             "\xff\xff\xff\xe0" => 27, "\xff\xff\xff\xf0" => 28, "\xff\xff\xff\xf8" => 29,
318             "\xff\xff\xff\xfc" => 30, "\xff\xff\xff\xfe" => 31, "\xff\xff\xff\xff" => 32,
319             );
320              
321             # pre-calced CIDR masks for /0 .. /128,
322             # e.g.
323             # $table_mask_n[3] == "\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
324             # $table_mask_n[64] == "\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00"
325             # $table_mask_n[128] == "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
326              
327             @table_mask_n = (
328             "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
329             "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
330             "\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
331             "\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
332             "\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
333             "\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
334             "\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
335             "\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
336             "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
337             "\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
338             "\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
339             "\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
340             "\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
341             "\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
342             "\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
343             "\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
344             "\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
345             "\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
346             "\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
347             "\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
348             "\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
349             "\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
350             "\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
351             "\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
352             "\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
353             "\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
354             "\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
355             "\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
356             "\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
357             "\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
358             "\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
359             "\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
360             "\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
361             "\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
362             "\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
363             "\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
364             "\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
365             "\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
366             "\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
367             "\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
368             "\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
369             "\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
370             "\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
371             "\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
372             "\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
373             "\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
374             "\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
375             "\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
376             "\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
377             "\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00",
378             "\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
379             "\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
380             "\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
381             "\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00",
382             "\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00",
383             "\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00",
384             "\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00",
385             "\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00",
386             "\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00",
387             "\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00",
388             "\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00",
389             "\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00",
390             "\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00",
391             "\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00",
392             "\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00",
393             "\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00",
394             "\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00",
395             "\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00",
396             "\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00",
397             "\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00",
398             "\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00",
399             "\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00",
400             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00",
401             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00",
402             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00",
403             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00",
404             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00",
405             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00",
406             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00",
407             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00",
408             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00",
409             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00",
410             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00",
411             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00",
412             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00",
413             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00",
414             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00",
415             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00",
416             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00",
417             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00",
418             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00",
419             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00",
420             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00",
421             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00",
422             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00",
423             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00",
424             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00",
425             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00",
426             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00",
427             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00",
428             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00",
429             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00",
430             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00",
431             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00",
432             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00",
433             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00",
434             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00",
435             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00",
436             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00",
437             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00",
438             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00",
439             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00",
440             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00",
441             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00",
442             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00",
443             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00",
444             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00",
445             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00",
446             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00",
447             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00",
448             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00",
449             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80",
450             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0",
451             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0",
452             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0",
453             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8",
454             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc",
455             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe",
456             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
457             );
458              
459             # pre-calced 'number of leading zeros' for byte values 0x00 - 0xff
460             # e.g.
461             # $nlz[0] == 8 # 0b0000_0000
462             # $nlz[1] == 7 # 0b0000_0001
463             # $nlz[64] == 1 # 0b0100_0000
464             # $nlz[128] == 0 # 0b1000_0000
465              
466             @nlz8 = (
467             8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, # 00..1f
468             2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, # 20..3f
469             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 40..5f
470             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 60..7f
471             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80..9f
472             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # a0..bf
473             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # c0..df
474             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # e0..ff
475             );
476              
477             ####################################
478             #>>> end of skip marker for perltidy
479             ####################################
480              
481             =head1 AUTHOR
482              
483             Karl Gaissmaier, C<< >>
484              
485             =head1 LICENSE AND COPYRIGHT
486              
487             This software is copyright (c) 2020-2022 by Karl Gaissmaier.
488              
489             This is free software; you can redistribute it and/or modify it under
490             the same terms as the Perl 5 programming language system itself.
491              
492             =cut
493              
494             1;