File Coverage

blib/lib/Net/IPAM/Block/Private.pm
Criterion Covered Total %
statement 134 134 100.0
branch 29 32 90.6
condition 23 25 92.0
subroutine 25 25 100.0
pod n/a
total 211 216 97.6


line stmt bran cond sub pod time code
1             package Net::IPAM::Block::Private;
2              
3 13     13   142 use 5.10.0;
  13         38  
4 13     13   57 use strict;
  13         21  
  13         213  
5 13     13   50 use warnings;
  13         17  
  13         261  
6 13     13   53 use utf8;
  13         27  
  13         53  
7              
8 13     13   308 use Carp ();
  13         27  
  13         235  
9 13     13   60 use Scalar::Util ();
  13         39  
  13         193  
10 13     13   61 use Net::IPAM::IP ();
  13         26  
  13         26243  
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   336 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         335 my $prefix_str = substr( $addr, 0, $idx );
37 227         359 my $suffix_str = substr( $addr, $idx + 1 );
38              
39 227   100     439 my $prefix = Net::IPAM::IP->new($prefix_str) // return;
40              
41 225         3377 my $mask_n;
42 225 100       374 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       30 return unless exists $valid_masks4{$mask_n};
46             }
47             else {
48 218         242 my $bits = 32;
49 218 100       360 $bits = 128 if $prefix->version == 6;
50              
51 218 100       1134 return unless $suffix_str =~ m/^\d+$/; # pos integer
52 216 100       420 return if $suffix_str > $bits;
53              
54 212         322 $mask_n = _make_mask_n( $suffix_str, $bits );
55             }
56              
57 217         450 $self->{base} = Net::IPAM::IP->new_from_bytes( _make_base_n( $prefix->bytes, $mask_n ) );
58 217         2652 $self->{last} = Net::IPAM::IP->new_from_bytes( _make_last_n( $prefix->bytes, $mask_n ) );
59              
60 217         2392 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 80     80   125 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 80         140 my $base_str = substr( $addr, 0, $idx );
70 80         123 my $last_str = substr( $addr, $idx + 1 );
71              
72 80   100     177 my $base_ip = Net::IPAM::IP->new($base_str) // return;
73 79   100     1332 my $last_ip = Net::IPAM::IP->new($last_str) // return;
74              
75             # version base != version last
76 77         991 my $version = $base_ip->version;
77 77 100       221 return if $version != $last_ip->version;
78              
79             # base > last?
80 75 100       252 return if $base_ip->cmp($last_ip) > 0;
81              
82 73         326 $self->{base} = $base_ip;
83 73         108 $self->{last} = $last_ip;
84              
85 73         200 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   90 my ( $self, $addr ) = @_;
92              
93 65   100     127 my $base = Net::IPAM::IP->new($addr) // return;
94              
95 63         964 $self->{base} = $base;
96 63         89 $self->{last} = $base;
97              
98 63         126 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     26 return unless Scalar::Util::blessed($ip) && $ip->isa('Net::IPAM::IP');
107              
108 4         9 $self->{base} = $ip;
109 4         5 $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         36 my $clone = bless {}, ref $self;
118              
119 19         43 $clone->{base} = Net::IPAM::IP->new_from_bytes( $self->{base}->bytes );
120 19         246 $clone->{last} = Net::IPAM::IP->new_from_bytes( $self->{last}->bytes );
121              
122 19         214 return $clone;
123             }
124              
125             # a.base <= b && a.last >= b
126             sub _contains_ip {
127 8   100 8   16 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   356 return 0 if $_[0]->cmp( $_[1] ) == 0;
134              
135             # a.base <= b.base && a.last >= b.last
136 247   100     1000 return $_[0]->{base}->cmp( $_[1]->{base} ) <= 0 && $_[0]->{last}->cmp( $_[1]->{last} ) >= 0;
137             }
138              
139             # count leading zeros
140             sub _leading_zeros {
141 430 50   430   675 my $n = shift or die 'missing arg,';
142 430         481 my $zeros = 0;
143              
144             # step byte-wise through $n from left to right
145 430         729 for my $i ( 0 .. length($n) - 1 ) {
146 2209         2375 my $byte = vec( $n, $i, 8 );
147              
148             # count bytes
149 2209 100       2842 if ( $byte == 0x00 ) {
150 1886         1916 $zeros += 8;
151 1886         2076 next;
152             }
153              
154             # count bits, use a pre-calculated table
155 323         386 $zeros += $nlz8[$byte];
156 323         361 last;
157             }
158              
159 430         1023 return $zeros;
160             }
161              
162             # number of common leading bits
163             sub _common_prefix {
164 430     430   2253 my ( $base_n, $last_n ) = @_;
165 430         792 return _leading_zeros( $base_n ^ $last_n );
166             }
167              
168             # recursion ahead
169             # end condition: is_cidr
170             # split the range in the middle
171             # call both halves recursively
172             #
173             sub _to_cidrs_rec {
174 23     23   34 my ( $self, $buf ) = @_;
175              
176 23 100       42 if ( $self->is_cidr ) {
177 16         28 push @$buf, $self;
178 16         32 return $buf;
179             }
180              
181 7         12 my $base_ip = $self->{base};
182 7         10 my $last_ip = $self->{last};
183              
184 7         13 my $base_n = $self->{base}->bytes;
185 7         29 my $last_n = $self->{last}->bytes;
186              
187 7         20 my $pfx = _common_prefix( $base_n, $last_n );
188 7         11 my $bits = 32;
189 7 100       14 $bits = 128 if $self->version == 6;
190              
191             # make next mask
192 7         29 my $mask_n = _make_mask_n( $pfx + 1, $bits );
193              
194             # split range with new mask
195 7         14 my $mid_last_n = _make_last_n( $base_n, $mask_n );
196 7         14 my $mid_base_n = _make_base_n( $last_n, $mask_n );
197              
198             # make IPs from bitstring
199 7         27 my $mid_last_ip = Net::IPAM::IP->new_from_bytes($mid_last_n);
200 7         89 my $mid_base_ip = Net::IPAM::IP->new_from_bytes($mid_base_n);
201              
202             # make two new blocks
203 7         64 my $b1 = bless {}, ref $self;
204 7         19 $b1->{base} = $base_ip;
205 7         10 $b1->{last} = $mid_last_ip;
206              
207 7         9 my $b2 = bless {}, ref $self;
208 7         11 $b2->{base} = $mid_base_ip;
209 7         9 $b2->{last} = $last_ip;
210              
211             # make rec-descent call with both halves
212 7         26 $buf = _to_cidrs_rec( $b1, $buf );
213 7         13 $buf = _to_cidrs_rec( $b2, $buf );
214              
215 7         15 return $buf;
216             }
217              
218             # returns true if base and last forms a CIDR range
219             sub _is_cidr {
220 248     248   278 my $self = shift;
221              
222 248 100       395 if ( $self->version == 4 ) {
223 149         518 return _is_cidr4( $self->{base}->bytes, $self->{last}->bytes );
224             }
225              
226 99         358 return _is_cidr6( $self->{base}->bytes, $self->{last}->bytes );
227             }
228              
229             sub _is_cidr4 {
230 149     149   675 my ( $base_n, $last_n ) = @_;
231              
232             # get the mask
233 149         211 my $pfx = _common_prefix( $base_n, $last_n );
234 149         286 my $mask_n = _strip_to_32_bits( $table_mask_n[$pfx] );
235              
236             # apply mask to base and last as AND and OR
237             # and check for all zeros and all ones => it's a CIDR
238 149         249 my $is_all_zeros = ( $base_n ^ ( $base_n & $mask_n ) ) eq _strip_to_32_bits( $table_mask_n[0] );
239 149         274 my $is_all_ones = ( $last_n | $mask_n ) eq _strip_to_32_bits( $table_mask_n[128] );
240              
241 149   100     676 return $is_all_zeros && $is_all_ones;
242             }
243              
244             sub _is_cidr6 {
245 99     99   468 my ( $base_n, $last_n ) = @_;
246              
247             # get the mask
248 99         143 my $pfx = _common_prefix( $base_n, $last_n );
249 99         151 my $mask_n = $table_mask_n[$pfx];
250              
251             # apply mask to base and last as AND and OR
252             # and check for all zeros and all ones => it's a CIDR
253 99         189 my $is_all_zeros = ( $base_n ^ ( $base_n & $mask_n ) ) eq $table_mask_n[0];
254 99         137 my $is_all_ones = ( $last_n | $mask_n ) eq $table_mask_n[128];
255              
256 99   100     397 return $is_all_zeros && $is_all_ones;
257             }
258              
259             # base = addr & netMask
260             sub _make_base_n {
261 233     233   702 my ( $addr_n, $mask_n ) = @_;
262 233         549 return $addr_n & $mask_n;
263             }
264              
265             # last = addr | hostMask
266             sub _make_last_n {
267 242     242   664 my ( $addr_n, $mask_n ) = @_;
268 242         553 return $addr_n | ~$mask_n;
269             }
270              
271             # cut off the bits after 32, used for IPv4
272 537     537   883 sub _strip_to_32_bits { return $_[0] & "\xff\xff\xff\xff" }
273              
274             # make CIDR mask from bits:
275             # (24, 32) => 0xffff_ff00
276             # (56, 128) => 0xffff_ffff_ffff_ff00_0000_0000_0000_0000
277             #
278             sub _make_mask_n {
279 230     230   341 my ( $n, $bits ) = @_;
280 230         295 my $mask_n = $table_mask_n[$n];
281             #
282             # cut to first 32 bits
283 230 100       321 if ( $bits == 32 ) {
284 90         137 return _strip_to_32_bits($mask_n);
285             }
286 140         215 return $mask_n;
287             }
288              
289             sub _get_mask_n {
290 2     2   3 my ( $base_ip, $last_ip ) = @_;
291              
292 2         5 my $pfx = _common_prefix( $base_ip->bytes, $last_ip->bytes );
293              
294 2         4 my $bits = 32;
295 2 50       3 $bits = 128 if $base_ip->version == 6;
296              
297 2         17 return _make_mask_n( $pfx, $bits );
298             }
299              
300             #############################
301             # pre-calc tables
302             #############################
303              
304             #<<< skip marker for perltidy
305              
306             # pre-calced masks for 0.0.0.0 - 255.255.255.255
307             # only IPv4 addresses representable as a sequence of 1 bits followed by 0 bits
308             # are allowed as masks as input
309              
310             %valid_masks4 = (
311             "\x00\x00\x00\x00" => 0, "\x80\x00\x00\x00" => 1, "\xc0\x00\x00\x00" => 2,
312             "\xe0\x00\x00\x00" => 3, "\xf0\x00\x00\x00" => 4, "\xf8\x00\x00\x00" => 5,
313             "\xfc\x00\x00\x00" => 6, "\xfe\x00\x00\x00" => 7, "\xff\x00\x00\x00" => 8,
314             "\xff\x80\x00\x00" => 9, "\xff\xc0\x00\x00" => 10, "\xff\xe0\x00\x00" => 11,
315             "\xff\xf0\x00\x00" => 12, "\xff\xf8\x00\x00" => 13, "\xff\xfc\x00\x00" => 14,
316             "\xff\xfe\x00\x00" => 15, "\xff\xff\x00\x00" => 16, "\xff\xff\x80\x00" => 17,
317             "\xff\xff\xc0\x00" => 18, "\xff\xff\xe0\x00" => 19, "\xff\xff\xf0\x00" => 20,
318             "\xff\xff\xf8\x00" => 21, "\xff\xff\xfc\x00" => 22, "\xff\xff\xfe\x00" => 23,
319             "\xff\xff\xff\x00" => 24, "\xff\xff\xff\x80" => 25, "\xff\xff\xff\xc0" => 26,
320             "\xff\xff\xff\xe0" => 27, "\xff\xff\xff\xf0" => 28, "\xff\xff\xff\xf8" => 29,
321             "\xff\xff\xff\xfc" => 30, "\xff\xff\xff\xfe" => 31, "\xff\xff\xff\xff" => 32,
322             );
323              
324             # pre-calced CIDR masks for /0 .. /128,
325             # e.g.
326             # $table_mask_n[3] == "\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
327             # $table_mask_n[64] == "\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00"
328             # $table_mask_n[128] == "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
329              
330             @table_mask_n = (
331             "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
332             "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
333             "\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
334             "\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
335             "\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
336             "\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
337             "\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
338             "\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
339             "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
340             "\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
341             "\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
342             "\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
343             "\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
344             "\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
345             "\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
346             "\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
347             "\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
348             "\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
349             "\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
350             "\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
351             "\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
352             "\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
353             "\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
354             "\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
355             "\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
356             "\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
357             "\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
358             "\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
359             "\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
360             "\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
361             "\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
362             "\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
363             "\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
364             "\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
365             "\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
366             "\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
367             "\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
368             "\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
369             "\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
370             "\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
371             "\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
372             "\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
373             "\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
374             "\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
375             "\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
376             "\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
377             "\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
378             "\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
379             "\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
380             "\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00",
381             "\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
382             "\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
383             "\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
384             "\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00",
385             "\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00",
386             "\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00",
387             "\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00",
388             "\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00",
389             "\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00",
390             "\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00",
391             "\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00",
392             "\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00",
393             "\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00",
394             "\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00",
395             "\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00",
396             "\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00",
397             "\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00",
398             "\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00",
399             "\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00",
400             "\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00",
401             "\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00",
402             "\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00",
403             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00",
404             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00",
405             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00",
406             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00",
407             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00",
408             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00",
409             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00",
410             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00",
411             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00",
412             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00",
413             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00",
414             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00",
415             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00",
416             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00",
417             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00",
418             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00",
419             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00",
420             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00",
421             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00",
422             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00",
423             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00",
424             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00",
425             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00",
426             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00",
427             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00",
428             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00",
429             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00",
430             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00",
431             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00",
432             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00",
433             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00",
434             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00",
435             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00",
436             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00",
437             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00",
438             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00",
439             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00",
440             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00",
441             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00",
442             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00",
443             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00",
444             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00",
445             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00",
446             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00",
447             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00",
448             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00",
449             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00",
450             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00",
451             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00",
452             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80",
453             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0",
454             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0",
455             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0",
456             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8",
457             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc",
458             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe",
459             "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
460             );
461              
462             # pre-calced 'number of leading zeros' for byte values 0x00 - 0xff
463             # e.g.
464             # $nlz[0] == 8 # 0b0000_0000
465             # $nlz[1] == 7 # 0b0000_0001
466             # $nlz[64] == 1 # 0b0100_0000
467             # $nlz[128] == 0 # 0b1000_0000
468              
469             @nlz8 = (
470             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
471             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
472             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
473             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
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, # 80..9f
475             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
476             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
477             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
478             );
479              
480             ####################################
481             #>>> end of skip marker for perltidy
482             ####################################
483              
484             =head1 AUTHOR
485              
486             Karl Gaissmaier, C<< >>
487              
488             =head1 LICENSE AND COPYRIGHT
489              
490             This software is copyright (c) 2020-2021 by Karl Gaissmaier.
491              
492             This is free software; you can redistribute it and/or modify it under
493             the same terms as the Perl 5 programming language system itself.
494              
495             =cut
496              
497             1;