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   146 use 5.10.0;
  13         40  
4 13     13   61 use strict;
  13         20  
  13         224  
5 13     13   51 use warnings;
  13         20  
  13         266  
6 13     13   55 use utf8;
  13         27  
  13         62  
7              
8 13     13   237 use Carp ();
  13         20  
  13         142  
9 13     13   55 use Scalar::Util ();
  13         23  
  13         155  
10 13     13   48 use Net::IPAM::IP ();
  13         22  
  13         28535  
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 228     228   359 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 228         383 my $prefix_str = substr( $addr, 0, $idx );
37 228         372 my $suffix_str = substr( $addr, $idx + 1 );
38              
39 228   100     462 my $prefix = Net::IPAM::IP->new($prefix_str) // return;
40              
41 226         3573 my $mask_n;
42 226 100       408 if ( index( $suffix_str, '.' ) >= 0 ) {
43 7   100     14 my $ip = Net::IPAM::IP->new($suffix_str) // return;
44 6         76 $mask_n = $ip->bytes;
45 6 100       34 return unless exists $valid_masks4{$mask_n};
46             }
47             else {
48 219         256 my $bits = 32;
49 219 100       389 $bits = 128 if $prefix->version == 6;
50              
51 219 100       1295 return unless $suffix_str =~ m/^\d+$/; # pos integer
52 217 100       471 return if $suffix_str > $bits;
53              
54 213         313 $mask_n = _make_mask_n( $suffix_str, $bits );
55             }
56              
57 218         452 $self->{base} = Net::IPAM::IP->new_from_bytes( _make_base_n( $prefix->bytes, $mask_n ) );
58 218         2706 $self->{last} = Net::IPAM::IP->new_from_bytes( _make_last_n( $prefix->bytes, $mask_n ) );
59              
60 218         2504 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   149 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         129 my $last_str = substr( $addr, $idx + 1 );
71              
72 81   100     197 my $base_ip = Net::IPAM::IP->new($base_str) // return;
73 80   100     1478 my $last_ip = Net::IPAM::IP->new($last_str) // return;
74              
75             # version base != version last
76 78         1082 my $version = $base_ip->version;
77 78 100       234 return if $version != $last_ip->version;
78              
79             # base > last?
80 76 100       316 return if $base_ip->cmp($last_ip) > 0;
81              
82 74         365 $self->{base} = $base_ip;
83 74         105 $self->{last} = $last_ip;
84              
85 74         205 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   111 my ( $self, $addr ) = @_;
92              
93 65   100     138 my $base = Net::IPAM::IP->new($addr) // return;
94              
95 63         1139 $self->{base} = $base;
96 63         89 $self->{last} = $base;
97              
98 63         139 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   9 my ( $self, $ip ) = @_;
105              
106 4 50 33     62 return unless Scalar::Util::blessed($ip) && $ip->isa('Net::IPAM::IP');
107              
108 4         13 $self->{base} = $ip;
109 4         7 $self->{last} = $ip;
110              
111 4         9 return $self;
112             }
113              
114             # my $b = _clone($self)
115             sub _clone {
116 19     19   27 my $self = shift;
117 19         34 my $clone = bless {}, ref $self;
118              
119 19         39 $clone->{base} = Net::IPAM::IP->new_from_bytes( $self->{base}->bytes );
120 19         230 $clone->{last} = Net::IPAM::IP->new_from_bytes( $self->{last}->bytes );
121              
122 19         213 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   423 return 0 if $_[0]->cmp( $_[1] ) == 0;
134              
135             # a.base <= b.base && a.last >= b.last
136 247   100     1077 return $_[0]->{base}->cmp( $_[1]->{base} ) <= 0 && $_[0]->{last}->cmp( $_[1]->{last} ) >= 0;
137             }
138              
139             # count leading zeros
140             sub _leading_zeros {
141 679 50   679   1107 my $n = shift or die 'missing arg,';
142 679         709 my $zeros = 0;
143              
144             # step byte-wise through $n from left to right
145 679         1149 for my $i ( 0 .. length($n) - 1 ) {
146 4490         4955 my $byte = vec( $n, $i, 8 );
147              
148             # count bytes
149 4490 100       5758 if ( $byte == 0x00 ) {
150 3919         3875 $zeros += 8;
151 3919         4340 next;
152             }
153              
154             # count bits, use a pre-calculated table
155 571         714 $zeros += $nlz8[$byte];
156 571         676 last;
157             }
158              
159 679         1346 return $zeros;
160             }
161              
162             # number of common leading bits
163             sub _common_prefix {
164 679     679   2705 my ( $base_n, $last_n ) = @_;
165 679         1238 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 504     504   723 my ( $base_n, $last_n, $bits ) = @_;
171              
172 504         689 my $pfx = _common_prefix( $base_n, $last_n );
173 504         804 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 504         851 my $is_all_zeros = ( $base_n ^ ( $base_n & $mask_n ) ) eq _make_mask_n( 0, $bits );
178 504         753 my $is_all_ones = ( $last_n | $mask_n ) eq _make_mask_n( 128, $bits );
179              
180 504   100     1520 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   13 my $self = shift;
196 10         15 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         70 push @stack, [ $self->{base}->bytes, $self->{last}->bytes ];
204              
205 10         70 while ( scalar @stack > 0 ) {
206 278         334 my $block_n = pop @stack;
207 278         314 my $base_n = $block_n->[0];
208 278         336 my $last_n = $block_n->[1];
209              
210 278         388 my ( $pfx, $is_cidr ) = _common_ok( $base_n, $last_n, $bits );
211              
212             # is this block already a CIDR?
213 278 100       420 if ($is_cidr) {
214              
215             # add block ro result
216 144         234 my $block = bless {}, ref $self;
217 144         282 $block->{base} = Net::IPAM::IP->new_from_bytes($base_n);
218 144         1627 $block->{last} = Net::IPAM::IP->new_from_bytes($last_n);
219              
220 144         1545 push @result, $block;
221 144         322 next;
222             }
223              
224             # split block in two halves, add 1 to common prefix
225 134         193 my $mask_n = _make_mask_n( $pfx + 1, $bits );
226              
227             # split range with new mask
228 134         198 my $mid_last_n = _make_last_n( $base_n, $mask_n );
229 134         208 my $mid_base_n = _make_base_n( $last_n, $mask_n );
230              
231             # make two new blocks
232 134         206 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         347 push @stack, $block2_n, $block1_n;
237             }
238              
239 10         26 return \@result;
240             }
241              
242             # returns true if base and last forms a CIDR range
243             sub _is_cidr {
244 226     226   283 my $self = shift;
245              
246 226         248 my $bits = 32;
247 226 100       385 $bits = 128 if $self->version == 6;
248              
249 226         909 my $base_n = $self->{base}->bytes;
250 226         777 my $last_n = $self->{last}->bytes;
251              
252 226         691 my ( $pfx, $is_cidr ) = _common_ok( $base_n, $last_n, $bits );
253 226         610 return $is_cidr;
254             }
255              
256             # base = addr & netMask
257             sub _make_base_n {
258 361     361   944 my ( $addr_n, $mask_n ) = @_;
259 361         808 return $addr_n & $mask_n;
260             }
261              
262             # last = addr | hostMask
263             sub _make_last_n {
264 370     370   909 my ( $addr_n, $mask_n ) = @_;
265 370         844 return $addr_n | ~$mask_n;
266             }
267              
268             # cut off the bits after 32, used for IPv4
269 516     516   999 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 1870     1870   2414 my ( $n, $bits ) = @_;
277 1870         2255 my $mask_n = $table_mask_n[$n];
278             #
279             # cut to first 32 bits
280 1870 100       2567 if ( $bits == 32 ) {
281 516         642 return _strip_to_32_bits($mask_n);
282             }
283 1354         1797 return $mask_n;
284             }
285              
286             sub _get_mask_n {
287 2     2   4 my ( $base_ip, $last_ip ) = @_;
288              
289 2         5 my $pfx = _common_prefix( $base_ip->bytes, $last_ip->bytes );
290              
291 2         4 my $bits = 32;
292 2 50       4 $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;