File Coverage

blib/lib/Data/HexDump/Range/Split.pm
Criterion Covered Total %
statement 28 457 6.1
branch 0 208 0.0
condition 0 54 0.0
subroutine 10 52 19.2
pod 0 4 0.0
total 38 775 4.9


line stmt bran cond sub pod time code
1              
2             package Data::HexDump::Range ; ## no critic (Modules::RequireFilenameMatchesPackage)
3              
4 2     2   8 use strict;
  2         3  
  2         68  
5 2     2   7 use warnings ;
  2         3  
  2         45  
6 2     2   6 use Carp ;
  2         2  
  2         122  
7              
8             BEGIN
9 2     2   29 {
10              
11 2         18 use Sub::Exporter -setup =>
12             {
13             exports => [ qw() ],
14             groups =>
15             {
16             all => [ qw() ],
17             }
18 2     2   8 };
  2         1  
19            
20 2     2   902 use vars qw ($VERSION);
  2         3  
  2         63  
21             }
22              
23             #-------------------------------------------------------------------------------
24              
25 2     2   8 use English qw( -no_match_vars ) ;
  2         2  
  2         11  
26              
27 2     2   593 use Readonly ;
  2         2  
  2         105  
28             Readonly my $EMPTY_STRING => q{} ;
29              
30 2     2   9 use Carp qw(carp croak confess) ;
  2         2  
  2         72  
31 2     2   1107 use Text::Pluralize ;
  2         1217  
  2         9134  
32              
33             #-------------------------------------------------------------------------------
34              
35             =head1 NAME
36              
37             Data::HexDump::Range::Split - Handles formating for Data::HexDump::Range
38              
39             =head1 SYNOPSIS
40              
41             =head1 DESCRIPTION
42              
43             =head1 DOCUMENTATION
44              
45             =head1 SUBROUTINES/METHODS
46              
47             Subroutines prefixed with B<[P]> are not part of the public API and shall not be used directly.
48              
49             =cut
50              
51             #-------------------------------------------------------------------------------
52              
53             sub split
54             {
55              
56             =head2 [P] split($collected_data)
57              
58             Split the collected data into lines
59              
60             I -
61              
62             =over 2
63              
64             =item * $container - Collected data
65              
66             =back
67              
68             I - An Array containing column elements
69              
70             I
71              
72             =cut
73              
74 0     0 0   my ($self, $collected_data) = @_ ;
75              
76 0 0         if($self->{ORIENTATION} =~ /^hor/)
77             {
78 0           return $self->_split_horizontal($collected_data) ;
79             }
80             else
81             {
82 0           return $self->_split_vertical($collected_data) ;
83             }
84             }
85              
86             #-------------------------------------------------------------------------------
87              
88             sub _split_horizontal
89             {
90              
91             =head2 [P] _split_horizontal($collected_data)
92              
93             Split the collected data into horizontal lines
94              
95             I -
96              
97             =over 2
98              
99             =item * $container - Collected data
100              
101             =back
102              
103             I - An Array containing column elements
104              
105             I
106              
107             =cut
108              
109              
110 0     0     my ($self, $collected_data) = @_ ;
111              
112 0           my @lines ;
113 0           my $line = {} ;
114 0           my $wrapped_line = 0 ;
115              
116 0           my $current_offset = 0 ;
117 0           my $total_dumped_data = $self->{OFFSET_START} ;
118 0           my $room_left = $self->{DATA_WIDTH} ;
119              
120 0           my $lines_since_header = 0 ;
121              
122 0           my $max_range_name_size = $self->{MAXIMUM_RANGE_NAME_SIZE} ;
123 0           my $user_information_size = $self->{MAXIMUM_USER_INFORMATION_SIZE} ;
124 0           my $range_source = ['?', 'white'] ;
125              
126 0           my @found_bitfields ;
127              
128 0           my $last_range = (grep {! $_->{IS_BITFIELD}}@{$collected_data})[-1] ;
  0            
  0            
129              
130 0           my @collected_data_to_dump = @{$collected_data} ;
  0            
131              
132 0 0         if($self->{OFFSET_START})
133             {
134 0           my $range = {} ;
135 0           $range->{NAME} = '>>' ;
136 0           $range->{DATA} = '?' x $self->{DATA_WIDTH} ;
137            
138 0           my $left_pad_size = $self->{OFFSET_START} % $self->{DATA_WIDTH} ;
139 0           my $aligned_start_offset = $self->{OFFSET_START} - $left_pad_size ;
140              
141             =pod
142              
143             =item * $self -
144              
145             =item * $visible - Boolean - wether the range elements will be visible or not. used for alignment
146              
147             =item * $range - the range structure created by Gather
148              
149             =item * $line - container for the range strings to be displayed
150              
151             =item * $last_range - Boolean - wether the range is the last one to be displayed
152              
153             =item * $total_dumped_data - Integer - the amount of total data dumped so far
154              
155             =item * $dumped_data - Integer - the amount of byte dumped from the range so far
156              
157             =item * $size_to_dump - Integer - the amount of data to extract from the range
158              
159             =item * $room_left - Integer - the amount of space left in the line for the dimped data
160              
161             =cut
162              
163 0           $self->_dump_range_horizontal(0, $range, $line, 0, $aligned_start_offset, 0, $left_pad_size, $self->{DATA_WIDTH}) ;
164            
165 0           $current_offset += $self->{OFFSET_START} ;
166 0           $room_left = $self->{DATA_WIDTH} - $left_pad_size ;
167             }
168            
169 0           while (my $range = shift @collected_data_to_dump)
170             {
171 0 0         my $data_length = defined $range->{DATA} ? length($range->{DATA}) : 0 ;
172 0 0         my ($start_quote, $end_quote) = $range->{IS_COMMENT} ? ('"', '"') : ('<', '>') ;
173            
174 0 0         $range->{SOURCE} = $range_source if $range->{IS_BITFIELD} ;
175            
176              
177 0 0         if($range->{IS_BITFIELD})
178             {
179 0           $range->{COLOR} = $range_source->[1] ;
180 0           push @found_bitfields, $self->get_bitfield_lines($range) ;
181 0           next ;
182             }
183            
184 0           $range->{COLOR} = $self->get_default_color($range->{COLOR}) ;
185            
186 0 0         if($room_left == $self->{DATA_WIDTH})
187             {
188 0           push @lines, @found_bitfields ;
189 0           @found_bitfields = () ;
190             }
191            
192             # remember what range we process in case next range is bitfield
193 0 0         unless($range->{IS_COMMENT})
194             {
195 0           $range_source = [$range->{NAME}, $range->{COLOR}] ;
196             }
197            
198 0           my $dumped_data = 0 ;
199            
200 0 0 0       if(0 == $data_length && $self->{DISPLAY_RANGE_NAME})
201             {
202 0           my $display_range_name = 0 ;
203            
204 0 0         if($range->{IS_COMMENT})
205             {
206 0 0         $display_range_name++ if $self->{DISPLAY_COMMENT_RANGE} ;
207             }
208             else
209             {
210 0 0         $display_range_name++ if $self->{DISPLAY_ZERO_SIZE_RANGE} ;
211             }
212            
213 0 0         if($display_range_name)
214             {
215 0           my $name_size_quoted = $max_range_name_size - 2 ;
216 0 0         $name_size_quoted = 2 if $name_size_quoted < 2 ;
217            
218 0           push @{$line->{RANGE_NAME}},
  0            
219             {
220             'RANGE_NAME' => $start_quote . sprintf("%.${name_size_quoted}s", $range->{NAME}) . $end_quote,
221             'RANGE_NAME_COLOR' => $range->{COLOR},
222             },
223             {
224             'RANGE_NAME_COLOR' => undef,
225             'RANGE_NAME' => ', ',
226             } ;
227             }
228             }
229            
230 0 0         if($range->{IS_HEADER})
231             {
232 0           $range->{NAME} = '@' . $range->{NAME} ;
233 0           $range->{DATA} = '0' x $self->{DATA_WIDTH} ;
234            
235             # justify on the right
236 0           $self->_dump_range_horizontal(0, $range, $line, $last_range, $current_offset, $dumped_data, $room_left, $room_left) ;
237 0           $line->{NEW_LINE}++ ;
238 0           push @lines, $line ;
239              
240             # display header
241 0           $line = {} ;
242 0           push @lines, $self->get_information(\@lines, $range->{COLOR}) ;
243            
244             # justify on the left
245 0           $line = {} ;
246            
247 0           my $left_pad_size = $self->{DATA_WIDTH} - $room_left ;
248 0           $self->_dump_range_horizontal(0, $range, $line, $last_range, $current_offset -$left_pad_size , $dumped_data, $left_pad_size, $self->{DATA_WIDTH}) ;
249            
250 0           next ;
251             }
252            
253 0 0         if($range->{IS_SKIP})
254             {
255 0           $range->{NAME} = '>>' . $range->{NAME} ;
256 0           $range->{DATA} = ' ' x $self->{DATA_WIDTH} ;
257            
258 0   0       my $size_to_dump = min($room_left, $data_length - $dumped_data) || 0 ;
259 0           $room_left -= $size_to_dump ;
260              
261             # justify on the right
262 0           $self->_dump_range_horizontal(0, $range, $line, $last_range, $current_offset, $dumped_data, $size_to_dump, $room_left) ;
263            
264 0           my $data_left = $data_length - $size_to_dump ;
265 0           $current_offset += $size_to_dump ;
266            
267 0 0         if ($data_left)
268             {
269             # justify on the left
270 0           $line->{NEW_LINE}++ ;
271 0           push @lines, $line ;
272            
273 0           my $lines_to_skip = int($data_left / $self->{DATA_WIDTH}) ;
274 0           my $data_bytes_on_line = $data_left - ($lines_to_skip * $self->{DATA_WIDTH}) ;
275 0           my $left_data_offset = $current_offset + ($lines_to_skip * $self->{DATA_WIDTH}) ;
276            
277 0           $line = {} ;
278 0           $self->_dump_range_horizontal(0, $range, $line, $last_range, $left_data_offset, $dumped_data, $data_bytes_on_line, $self->{DATA_WIDTH}) ;
279            
280 0           $room_left = $self->{DATA_WIDTH} - $data_bytes_on_line ;
281 0           $current_offset += $data_left ;
282             }
283            
284 0           next ;
285             }
286            
287 0           while ($dumped_data < $data_length)
288             {
289 0   0       my $size_to_dump = min($room_left, $data_length - $dumped_data) || 0 ;
290            
291 0           $room_left -= $size_to_dump ;
292            
293 0           $self->_dump_range_horizontal(1, $range, $line, $last_range, $current_offset, $dumped_data, $size_to_dump, $room_left) ;
294            
295 0           $dumped_data += $size_to_dump ;
296 0           $current_offset += $size_to_dump ;
297            
298 0 0 0       if($room_left == 0 || $last_range == $range)
299             {
300 0           $line->{NEW_LINE}++ ;
301 0           push @lines, $line ;
302            
303 0           $line = {} ;
304 0           $room_left = $self->{DATA_WIDTH} ;
305            
306 0           push @lines, @found_bitfields ;
307 0           @found_bitfields = () ;
308             }
309             }
310             }
311              
312 0 0         if(@found_bitfields)
313             {
314 0           push @lines, @found_bitfields ;
315 0           @found_bitfields = () ;
316             }
317              
318 0           return \@lines ;
319             }
320              
321             #-------------------------------------------------------------------------------
322              
323             sub _split_vertical
324             {
325              
326             =head2 [P] _split_vertical($collected_data)
327              
328             Split the collected data into vertical lines
329              
330             I -
331              
332             =over 2
333              
334             =item * $container - Collected data
335              
336             =back
337              
338             I - An Array containing column elements
339              
340             I
341              
342             =cut
343              
344 0     0     my ($self, $collected_data) = @_ ;
345              
346 0           my @lines ;
347 0           my $line = {} ;
348 0           my $wrapped_line = 0 ;
349              
350 0           my $current_offset = 0 ;
351 0           my $total_dumped_data = $self->{OFFSET_START} ;
352 0           my $room_left = $self->{DATA_WIDTH} ;
353              
354 0           my $lines_since_header = 0 ;
355              
356 0           my $max_range_name_size = $self->{MAXIMUM_RANGE_NAME_SIZE} ;
357 0           my $user_information_size = $self->{MAXIMUM_USER_INFORMATION_SIZE} ;
358 0           my $range_source = ['?', 'white'] ;
359              
360 0           my @found_bitfields ;
361              
362 0           my $last_range = (grep {! $_->{IS_BITFIELD}}@{$collected_data})[-1] ;
  0            
  0            
363              
364 0           my @collected_data_to_dump = @{$collected_data} ;
  0            
365              
366 0           while (my $range = shift @collected_data_to_dump)
367             {
368 0 0         my $data_length = defined $range->{DATA} ? length($range->{DATA}) : 0 ;
369 0 0         my ($start_quote, $end_quote) = $range->{IS_COMMENT} ? ('"', '"') : ('<', '>') ;
370            
371 0 0         $range->{SOURCE} = $range_source if $range->{IS_BITFIELD} ;
372            
373             # vertical mode
374            
375 0           $range->{COLOR} = $self->get_default_color($range->{COLOR}) ;
376            
377 0           $line = {} ;
378              
379 0           my $dumped_data = 0 ;
380 0           my $current_range = '' ;
381            
382 0 0 0       if(!$range->{IS_BITFIELD} && 0 == $data_length && $self->{DISPLAY_RANGE_NAME}) # && $self->{DISPLAY_RANGE_NAME})
      0        
383             {
384 0           my $display_range_name = 0 ;
385            
386 0 0         if($range->{IS_COMMENT})
387             {
388 0 0         $display_range_name++ if $self->{DISPLAY_COMMENT_RANGE} ;
389             }
390             else
391             {
392 0 0         $display_range_name++ if $self->{DISPLAY_ZERO_SIZE_RANGE} ;
393             }
394            
395 0 0         if($display_range_name)
396             {
397 0           push @{$line->{RANGE_NAME}},
  0            
398             {
399             'RANGE_NAME_COLOR' => $range->{COLOR},
400             'RANGE_NAME' => "$start_quote$range->{NAME}$end_quote",
401             } ;
402            
403 0           $line->{NEW_LINE} ++ ;
404 0           push @lines, $line ;
405 0           $line = {};
406             }
407             }
408            
409 0 0         if($range->{IS_HEADER})
410             {
411             # display the header
412 0           push @lines, $self->get_information(\@lines, $range->{COLOR}) ;
413 0           next ;
414             }
415            
416 0 0         if($range->{IS_SKIP})
417             {
418 0           my $next_data_offset = $total_dumped_data + $data_length - 1 ;
419            
420 0           $range->{NAME} = '>>' . $range->{NAME} ;
421            
422 0     0     for my $field_type
  0            
423             (
424 0     0     ['RANGE_NAME', sub {sprintf "%-${max_range_name_size}.${max_range_name_size}s", $range->{NAME} }, $range->{COLOR}, $max_range_name_size] ,
425 0     0     ['OFFSET', sub {sprintf $self->{OFFSET_FORMAT}, $total_dumped_data}, undef, 8],
426 0     0     ['CUMULATIVE_OFFSET', sub {sprintf $self->{OFFSET_FORMAT}, $next_data_offset}, undef, 8],
427             ['BITFIELD_SOURCE', sub {' ' x 8}, undef, 8],
428             [
429             'HEX_DUMP',
430             sub
431             {
432 0     0     my @bytes = unpack("(H2)*", pack("N", $data_length));
433 0           pluralize("Skipped @bytes byte(s)", $data_length) ;
434             },
435             $range->{COLOR},
436             3 * $self->{DATA_WIDTH},
437             ],
438             [
439             'HEXASCII_DUMP',
440             sub
441             {
442 0     0     my @bytes = unpack("(H2)*", pack("N", $data_length));
443 0           pluralize("Skipped @bytes byte(s)", $data_length) ;
444             },
445             $range->{COLOR},
446             3 * $self->{DATA_WIDTH},
447             ],
448             [
449             'DEC_DUMP',
450             sub
451             {
452 0     0     pluralize("Skipped $data_length byte(s)", $data_length) ;
453             },
454 0     0     $range->{COLOR},
455             4 * $self->{DATA_WIDTH}
456             ],
457             ['ASCII_DUMP', sub {$EMPTY_STRING}, $range->{COLOR}, $self->{DATA_WIDTH}],
458 0   0 0     ['USER_INFORMATION', sub { sprintf '%-20.20s', $range->{USER_INFORMATION} || ''}, $range->{COLOR}, 20],
459             )
460             {
461 0           my ($field_name, $field_data_formater, $color, $field_text_size) = @{$field_type} ;
  0            
462            
463 0 0         if($self->{"DISPLAY_$field_name"})
464             {
465 0           my $field_text = $field_data_formater->([]) ;
466 0           my $pad = ' ' x ($field_text_size - length($field_text)) ;
467            
468 0           push @{$line->{$field_name}},
  0            
469             {
470             $field_name . '_COLOR' => $color,
471             $field_name => $field_text . $pad,
472             } ;
473             }
474             }
475            
476 0           $total_dumped_data += $data_length ;
477            
478 0           $line->{NEW_LINE} ++ ;
479 0           push @lines, $line ;
480 0           $line = {};
481            
482 0           next ;
483             }
484            
485 0           while ($dumped_data < $data_length)
486             {
487 0 0         last if($range->{IS_BITFIELD}) ;
488              
489 0           my $left_offset = $total_dumped_data % $self->{DATA_WIDTH} ;
490            
491 0 0         if($left_offset)
492             {
493             # previous range did not end on DATA_WIDTH offset, align
494 0           local $range->{DATA} = '0' x $self->{DATA_WIDTH} ;
495            
496 0           $self->_dump_range_vertical(0, $range, $line, 0, 0, $left_offset) ;
497            
498 0           $room_left -= $left_offset ;
499             }
500            
501 0           my $size_to_dump = min($room_left, length($range->{DATA}) - $dumped_data) ;
502 0           $room_left -= $size_to_dump ;
503 0           $self->_dump_range_vertical(1, $range, $line, $dumped_data, $total_dumped_data, $size_to_dump) ;
504            
505 0 0         if($room_left)
506             {
507 0           local $range->{DATA} = '0' x $self->{DATA_WIDTH} ;
508            
509 0           $self->_dump_range_vertical(0, $range, $line, 0, 0, $room_left) ;
510            
511 0           $room_left = 0 ;
512             }
513            
514 0           $dumped_data += $size_to_dump ;
515 0           $total_dumped_data += $size_to_dump ;
516              
517 0           $line->{NEW_LINE} ++ ;
518 0           push @lines, $line ;
519 0           $line = {};
520 0           $room_left = $self->{DATA_WIDTH} ;
521             }
522            
523 0 0         if($range->{IS_BITFIELD})
524             {
525 0           push @lines, $self->get_bitfield_lines($range) ;
526             }
527             else
528             {
529 0           $range_source = [$range->{NAME}, $range->{COLOR}] ;
530             }
531             }
532              
533 0 0         if(@found_bitfields)
534             {
535 0           push @lines, @found_bitfields ;
536 0           @found_bitfields = () ;
537             }
538              
539 0           return \@lines ;
540             }
541              
542             #-------------------------------------------------------------------------------
543              
544             sub _dump_range_horizontal
545             {
546             =head2 [P] _dump_range_horizontal(...)
547              
548             Splits a range into a structure used for horizontal display
549              
550             I -
551              
552             =over 2
553              
554             =item * $self -
555              
556             =item * $visible - Boolean - wether the range elements will be visible or not. used for alignment
557              
558             =item * $range - the range structure created by Gather
559              
560             =item * $line - container for the range strings to be displayed
561              
562             =item * $last_range - Boolean - wether the range is the last one to be displayed
563              
564             =item * $total_dumped_data - Integer - the amount of total data dumped so far
565              
566             =item * $dumped_data - Integer - the amount of byte dumped from the range so far
567              
568             =item * $size_to_dump - Integer - the amount of data to extract from the range
569              
570             =item * $room_left - Integer - the amount of space left in the line for the dimped data
571              
572             =back
573              
574             I - Nothing. Stores the result in the $line argument
575              
576             I
577              
578             =cut
579              
580 0     0     my ($self, $visible, $range, $line, $last_range, $total_dumped_data, $dumped_data, $size_to_dump, $room_left) = @_ ;
581              
582 0           my @range_unpacked_data = unpack("x$dumped_data C$size_to_dump", $range->{DATA}) ;
583 0           my $max_range_name_size = $self->{MAXIMUM_RANGE_NAME_SIZE} ;
584            
585 0 0   0     for my $field_type
  0            
586             (
587 0 0   0     ['OFFSET', sub {exists $line->{OFFSET} ? '' : sprintf $self->{OFFSET_FORMAT}, $total_dumped_data}, $self->get_bg_color(), 0],
588 0     0     ['BITFIELD_SOURCE', sub {exists $line->{BITFIELD_SOURCE} ? '' : ' ' x 8}, $self->get_bg_color(), 0],
589 0     0     ['HEX_DUMP', sub {sprintf '%02x ' x $size_to_dump, @_}, $range->{COLOR}, 3],
590 0 0         ['DEC_DUMP', sub {sprintf '%03u ' x $size_to_dump, @_}, $range->{COLOR}, 4],
591 0 0   0     ['ASCII_DUMP', sub {sprintf '%c' x $size_to_dump, map{$_ < 30 ? ord('.') : $_ } @_}, $range->{COLOR}, 1],
  0            
592 0     0     ['HEXASCII_DUMP', sub {sprintf q~%02x/%c ~ x $size_to_dump, map{$_ < 30 ? ($_, ord('.')) : ($_, $_) } @_}, $range->{COLOR}, 5],
  0            
593 0     0     ['RANGE_NAME',sub {sprintf "%.${max_range_name_size}s", $range->{NAME}}, $range->{COLOR}, 0],
594             ['RANGE_NAME', sub {', '}, undef, 0],
595             )
596             {
597 0           my ($field_name, $field_data_formater, $color, $pad_size) = @{$field_type} ;
  0            
598            
599 0 0         if($self->{"DISPLAY_$field_name"})
600             {
601 0           my $field_text = $field_data_formater->(@range_unpacked_data) ;
602            
603 0 0         my $pad = $last_range == $range ? $pad_size ? ' ' x ($room_left * $pad_size) : '' : '' ;
    0          
604            
605 0           my $text = $field_text . $pad ;
606              
607 0 0         unless($visible)
608             {
609 0 0 0       if($field_name eq 'ASCII_DUMP' || $field_name eq 'HEX_DUMP' || $field_name eq 'HEXASCII_DUMP' || $field_name eq 'DEC_DUMP' )
      0        
      0        
610             {
611 0           $text = ' ' x length($text)
612             }
613             }
614            
615 0           push @{$line->{$field_name}},
  0            
616             {
617             $field_name . '_COLOR' => $color,
618             $field_name => $text
619             } ;
620             }
621             }
622             }
623              
624             #-------------------------------------------------------------------------------
625              
626             sub _dump_range_vertical
627             {
628             =head2 [P] _dump_range_vertical()
629              
630             Splits a range into a structure used for vertical display
631              
632             I -
633              
634             =over 2
635              
636             =item * $self -
637              
638             =item * $visible - Boolean - wether the range elements will be visible or not. used for alignment
639              
640             =item * $range - the range structure created by Gather
641              
642             =item * $line - container for the range strings to be displayed
643              
644             =item * $dumped_data - Integer - the amount of byte dumped from the range so far
645              
646             =item * $total_dumped_data - Integer - the amount of total data dumped so far
647              
648             =item * $size_to_dump - Integer - the amount of data to extract from the range
649              
650             =back
651              
652             I -
653              
654             I
655              
656             =cut
657              
658 0     0     my ($self, $visible, $range, $line, $dumped_data, $total_dumped_data, $size_to_dump) = @_ ;
659              
660 0           my @range_data = unpack("x$dumped_data C$size_to_dump", $range->{DATA}) ;
661 0           my $max_range_name_size = $self->{MAXIMUM_RANGE_NAME_SIZE} ;
662 0           my $user_information_size = $self->{MAXIMUM_USER_INFORMATION_SIZE} ;
663              
664 0     0     for my $field_type
  0            
665             (
666 0     0     ['RANGE_NAME', sub {sprintf "%-${max_range_name_size}.${max_range_name_size}s", $range->{NAME} ; }, $range->{COLOR}, $max_range_name_size] ,
667 0 0   0     ['OFFSET', sub {sprintf $self->{OFFSET_FORMAT}, $total_dumped_data}, $self->get_bg_color(), 8],
668 0     0     ['CUMULATIVE_OFFSET', sub {$dumped_data ? sprintf($self->{OFFSET_FORMAT}, $dumped_data) : ''}, $self->get_bg_color(), 8],
669 0           ['BITFIELD_SOURCE', sub {'' x 8}, undef, 8],
670 0 0   0     ['HEX_DUMP', sub {sprintf '%02x ' x $size_to_dump, @{$_[0]}}, $range->{COLOR}, 3 * $size_to_dump],
  0            
671 0     0     ['HEXASCII_DUMP', sub {sprintf q~%02x/%c ~ x $size_to_dump, map{$_ < 30 ? ($_, ord('.')) : ($_, $_) } @{ $_[0]}}, $range->{COLOR}, 5 * $size_to_dump],
  0            
  0            
672 0 0   0     ['DEC_DUMP', sub {sprintf '%03u ' x $size_to_dump, @{ $_[0] }}, $range->{COLOR}, 4 * $size_to_dump],
  0            
673 0     0     ['ASCII_DUMP', sub {sprintf '%c' x $size_to_dump, map{$_ < 30 ? ord('.') : $_ } @{$_[0]}}, $range->{COLOR}, $size_to_dump],
  0            
674 0   0 0     ['USER_INFORMATION', sub { sprintf "%-${user_information_size}.${user_information_size}s", $range->{USER_INFORMATION} || ''}, $range->{COLOR}, $user_information_size],
675             )
676             {
677 0           my ($field_name, $field_data_formater, $color, $field_text_size) = @{$field_type} ;
  0            
678            
679 0 0         if($self->{"DISPLAY_$field_name"})
680             {
681 0           my $field_text = $field_data_formater->(\@range_data) ;
682 0           my $pad = ' ' x ($field_text_size - length($field_text)) ;
683            
684 0           my $text = $field_text . $pad ;
685            
686 0 0         unless($visible)
687             {
688 0 0 0       if($field_name eq 'ASCII_DUMP' || $field_name eq 'HEX_DUMP' || $field_name eq 'DEC_DUMP' || $field_name eq 'HEXASCII_DUMP' )
      0        
      0        
689             {
690 0           $text = ' ' x length($text) ;
691             }
692             else
693             {
694 0           $text = '' ;
695             }
696             }
697            
698 0           push @{$line->{$field_name}},
  0            
699             {
700             $field_name . '_COLOR' => $color,
701             $field_name => $text,
702             } ;
703             }
704             }
705             }
706              
707             #-------------------------------------------------------------------------------
708              
709             sub get_bitfield_lines
710             {
711              
712             =head2 [P] get_bitfield_lines($bitfield_description)
713              
714             Split the collected data into lines
715              
716             I -
717              
718             =over 2
719              
720             =item * $self - a Data::HexDump::Range object
721              
722             =item * $bitfield_description -
723              
724             =back
725              
726             I - An Array containing column elements,
727              
728             I None but will embed an error in the element if any is found
729              
730             =cut
731              
732 0     0 0   my ($self, $bitfield_description) = @_ ;
733              
734             #~ use Data::TreeDumper ;
735             #~ print DumpTree $bitfield_description, '$bitfield_description', QUOTE_VALUES => 1 ;
736              
737 0 0         return unless $self->{DISPLAY_BITFIELDS} ;
738              
739 0           my ($line, @lines) = ({}) ;
740 0           my $digits_or_hex = '(?:(?:0x[0-9a-fA-F]+)|(?:\d+))' ;
741              
742 0           my ($byte_offset, $offset, $size) = $bitfield_description->{IS_BITFIELD} =~ /^\s*(X$digits_or_hex)?\s*(x$digits_or_hex)?\s*(b$digits_or_hex)\s*$/ ;
743              
744 0 0         if(defined $byte_offset)
745             {
746 0           substr($byte_offset, 0, 1, '') ;
747 0 0         $byte_offset = hex($byte_offset) if $byte_offset=~ /^0x/ ;
748             }
749            
750 0 0         if(defined $offset)
751             {
752 0           substr($offset, 0, 1, '') ;
753 0 0         $offset = hex($offset) if $offset=~ /^0x/ ;
754             }
755            
756 0 0         if(defined $size)
757             {
758 0           substr($size, 0, 1, '') ;
759 0 0         $size = hex($size) if $size =~ /^0x/ ;
760             }
761              
762 0   0       $byte_offset ||= 0 ;
763 0   0       $offset ||= 0 ; $offset += $byte_offset * 8 ;
  0            
764 0   0       $size ||= 1 ;
765              
766 0           my $max_range_name_size = $self->{MAXIMUM_RANGE_NAME_SIZE} ;
767 0           my $max_bitfield_source_size = $self->{MAXIMUM_BITFIELD_SOURCE_SIZE} ;
768              
769 0           my %always_display_field = map {$_ => 1} qw(RANGE_NAME OFFSET CUMULATIVE_OFFSET BITFIELD_SOURCE USER_INFORMATION) ;
  0            
770 0           my $bitfield_warning_displayed = 0 ;
771              
772             #~ print DumpTree {length => length($bitfield_description->{DATA}), offset => $offset, size => $size, BF => $bitfield_description} ;
773             my $ascii_bitfield_dump_sub =
774             sub
775             {
776 0     0     my ($binary, @binary , @chars) ;
777            
778 0 0         if($self->{BIT_ZERO_ON_LEFT})
779             {
780 0           @binary = split '', unpack("B*", $_[0]->{DATA}) ;
781 0           splice(@binary, 0, $offset) ;
782 0           splice(@binary, $size) ;
783             }
784             else
785             {
786 0           @binary = split '', unpack("B*", $_[0]->{DATA}) ;
787 0 0         splice(@binary, -$offset) unless $offset == 0 ;
788 0           @binary = splice(@binary, - $size) ;
789             }
790            
791 0           $binary = join('', @binary) ;
792 0 0         @chars = map{$_ < 30 ? '.' : chr($_) } unpack("C*", pack("B32", substr("0" x 32 . $binary, -32)));
  0            
793            
794 0 0         my $number_of_bytes = @binary > 24 ? 4 : @binary > 16 ? 3 : @binary > 8 ? 2 : 1 ;
    0          
    0          
795 0           splice @chars, 0 , (4 - $number_of_bytes), map {'-'} 1 .. (4 - $number_of_bytes) ;
  0            
796            
797 0           '.bitfield: '. join('', @chars)
798 0           } ;
799              
800 0     0     for my $field_type
  0            
801             (
802 0     0     ['RANGE_NAME', sub {sprintf "%-${max_range_name_size}.${max_range_name_size}s", '.' . $_[0]->{NAME} ; }, undef, $max_range_name_size ] ,
803 0     0     ['OFFSET', sub {sprintf '%02u .. %02u', $offset, ($offset + $size) - 1}, undef, 8],
804 0     0     ['CUMULATIVE_OFFSET', sub {''}, undef, 8],
805             ['BITFIELD_SOURCE', sub {sprintf "%-${max_bitfield_source_size}.${max_bitfield_source_size}s", $_[0]->{SOURCE}[0]}, $bitfield_description->{SOURCE}[1], 8],
806             ['HEX_DUMP',
807             sub
808             {
809 0     0     my ($binary, @binary , $binary_dashed) ;
810            
811 0 0         if($self->{BIT_ZERO_ON_LEFT})
812             {
813 0           @binary = split '', unpack("B*", $_[0]->{DATA}) ;
814 0           splice(@binary, 0, $offset) ;
815 0           splice(@binary, $size) ;
816            
817 0           $binary = join('', @binary) ;
818            
819 0           $binary_dashed = '-' x $offset . $binary . '-' x (32 - ($size + $offset)) ;
820 0           $binary_dashed = substr($binary_dashed , -32) ;
821 0           $binary_dashed = substr($binary_dashed, 0, 8) . ' ' . substr($binary_dashed, 8, 8) . ' ' .substr($binary_dashed, 16, 8) . ' ' .substr($binary_dashed, 24, 8) ;
822             }
823             else
824             {
825 0           @binary = split '', unpack("B*", $_[0]->{DATA}) ;
826 0 0         splice(@binary, -$offset) unless $offset == 0 ;
827 0           @binary = splice(@binary, - $size) ;
828            
829 0           $binary = join('', @binary) ;
830            
831 0           $binary_dashed = '-' x (32 - ($size + $offset)) . $binary . '-' x $offset ;
832 0           $binary_dashed = substr($binary_dashed , 0, 32) ;
833 0           $binary_dashed = substr($binary_dashed, 0, 8) . ' ' . substr($binary_dashed, 8, 8) . ' ' .substr($binary_dashed, 16, 8) . ' ' .substr($binary_dashed, 24, 8) ;
834             }
835            
836 0 0         my $bytes = $size > 24 ? 4 : $size > 16 ? 3 : $size > 8 ? 2 : 1 ;
    0          
    0          
837            
838 0           my @bytes = unpack("(H2)*", pack("B32", substr("0" x 32 . $binary, -32)));
839            
840 0 0         my $number_of_bytes = @binary > 24 ? 4 : @binary > 16 ? 3 : @binary > 8 ? 2 : 1 ;
    0          
    0          
841 0           splice @bytes, 0 , (4 - $number_of_bytes), map {'--'} 1 .. (4 - $number_of_bytes) ;
  0            
842            
843 0           join(' ', @bytes) . ' ' . $binary_dashed;
844             },
845            
846             undef, 3 * $self->{DATA_WIDTH}],
847             ['HEXASCII_DUMP',
848             sub
849             {
850 0     0     my $ascii_bitfield_dump = $ascii_bitfield_dump_sub->(@_) ;
851            
852              
853 0           my ($binary, @binary , $binary_dashed) ;
854            
855 0 0         if($self->{BIT_ZERO_ON_LEFT})
856             {
857 0           @binary = split '', unpack("B*", $_[0]->{DATA}) ;
858 0           splice(@binary, 0, $offset) ;
859 0           splice(@binary, $size) ;
860            
861 0           $binary = join('', @binary) ;
862            
863 0           $binary_dashed = '-' x $offset . $binary . '-' x (32 - ($size + $offset)) ;
864 0           $binary_dashed = substr($binary_dashed , -32) ;
865 0           $binary_dashed = substr($binary_dashed, 0, 8) . ' ' . substr($binary_dashed, 8, 8) . ' ' .substr($binary_dashed, 16, 8) . ' ' .substr($binary_dashed, 24, 8) ;
866             }
867             else
868             {
869 0           @binary = split '', unpack("B*", $_[0]->{DATA}) ;
870 0 0         splice(@binary, -$offset) unless $offset == 0 ;
871 0           @binary = splice(@binary, - $size) ;
872            
873 0           $binary = join('', @binary) ;
874            
875 0           $binary_dashed = '-' x (32 - ($size + $offset)) . $binary . '-' x $offset ;
876 0           $binary_dashed = substr($binary_dashed , 0, 32) ;
877 0           $binary_dashed = substr($binary_dashed, 0, 8) . ' ' . substr($binary_dashed, 8, 8) . ' ' .substr($binary_dashed, 16, 8) . ' ' .substr($binary_dashed, 24, 8) ;
878             }
879            
880 0 0         my $bytes = $size > 24 ? 4 : $size > 16 ? 3 : $size > 8 ? 2 : 1 ;
    0          
    0          
881            
882 0           my @bytes = unpack("(H2)*", pack("B32", substr("0" x 32 . $binary, -32)));
883            
884 0 0         my $number_of_bytes = @binary > 24 ? 4 : @binary > 16 ? 3 : @binary > 8 ? 2 : 1 ;
    0          
    0          
885 0           splice @bytes, 0 , (4 - $number_of_bytes), map {'--'} 1 .. (4 - $number_of_bytes) ;
  0            
886            
887 0           join(' ', @bytes) . ' ' . $binary_dashed . ' ' . $ascii_bitfield_dump ;
888              
889            
890             },
891            
892             undef, 5 * $self->{DATA_WIDTH}],
893             ['DEC_DUMP',
894             sub
895             {
896 0     0     my ($binary, @binary , $value) ;
897            
898 0 0         if($self->{BIT_ZERO_ON_LEFT})
899             {
900 0           @binary = split '', unpack("B*", $_[0]->{DATA}) ;
901 0           splice(@binary, 0, $offset) ;
902 0           splice(@binary, $size) ;
903 0           $binary = join('', @binary) ;
904 0           $value = unpack("N", pack("B32", substr("0" x 32 . $binary, -32)));
905             }
906             else
907             {
908 0           @binary = split '', unpack("B*", $_[0]->{DATA}) ;
909 0 0         splice(@binary, -$offset) unless $offset == 0 ;
910 0           @binary = splice(@binary, - $size) ;
911 0           $binary = join('', @binary) ;
912 0           $value = unpack("N", pack("B32", substr("0" x 32 . $binary, -32)));
913             }
914            
915 0           my @values = map {sprintf '%03u', $_} unpack("W*", pack("B32", substr("0" x 32 . $binary, -32)));
  0            
916            
917 0 0         my $number_of_bytes = @binary > 24 ? 4 : @binary > 16 ? 3 : @binary > 8 ? 2 : 1 ;
    0          
    0          
918 0           splice @values, 0 , (4 - $number_of_bytes), map {'---'} 1 .. (4 - $number_of_bytes) ;
  0            
919            
920 0           join(' ', @values) . ' ' . "value: $value" ;
921             },
922            
923             $bitfield_description->{COLOR}, 4 * $self->{DATA_WIDTH}],
924            
925             ['ASCII_DUMP',
926             $ascii_bitfield_dump_sub,
927             undef, $self->{DATA_WIDTH}],
928            
929             ['USER_INFORMATION',
930 0   0 0     sub { sprintf '%-20.20s', $_[0]->{USER_INFORMATION} || ''},
931             $bitfield_description->{COLOR}, 20],
932            
933             )
934             {
935 0           my ($field_name, $field_data_formater, $color, $field_text_size) = @{$field_type} ;
  0            
936            
937             #~ print "($field_name, $field_data_formater, $color, $field_text_size)\n";
938 0   0       $color ||= $bitfield_description->{COLOR} ;
939            
940 0 0         if($self->{"DISPLAY_$field_name"})
941             {
942 0           my ($bitfield_error, $field_text) = (0) ;
943            
944 0 0         if($always_display_field{$field_name})
945             {
946 0           $field_text = $field_data_formater->($bitfield_description) ;
947             }
948             else
949             {
950 0 0         if($size > 32)
    0          
    0          
951             {
952 0 0         $self->{INTERACTION}{WARN}
953             (
954             "Warning: bitfield description '$bitfield_description->{NAME}' is more than 32 bits long ($size)\n"
955             ) unless $bitfield_warning_displayed++ ;
956            
957 0           $field_text = sprintf("%.${field_text_size}s", "Error: bitfield is more than 32 bits long ($size)") ;
958             }
959             elsif($EMPTY_STRING eq $bitfield_description->{DATA})
960             {
961 0 0         $self->{INTERACTION}{WARN}
962             (
963             "Warning: bitfield description '$bitfield_description->{NAME}' can't be applied to empty source\n"
964             ) unless $bitfield_warning_displayed++ ;
965            
966 0           $field_text = sprintf("%.${field_text_size}s", "Error: Empty source") ;
967             }
968             elsif(length($bitfield_description->{DATA}) * 8 < ($offset + $size))
969             {
970 0           my $bits_missing_message = ($offset + $size) . " bits needed but only " . length($bitfield_description->{DATA}) * 8 . ' bits available' ;
971            
972 0 0         $self->{INTERACTION}{WARN}
973             (
974             "Warning: bitfield description '$bitfield_description->{NAME}' can't be applied "
975             . "to source '$bitfield_description->{SOURCE}[0]':\n"
976             . "\t$bits_missing_message\n"
977             ) unless $bitfield_warning_displayed++ ;
978            
979 0           $field_text = sprintf("%.${field_text_size}s", 'Error: ' . $bits_missing_message) ;
980             }
981             else
982             {
983 0           $field_text = $field_data_formater->($bitfield_description) ;
984             }
985             }
986            
987 0           my $pad_size = $field_text_size - length($field_text) ;
988 0           push @{$line->{$field_name}},
  0            
989             {
990             $field_name . '_COLOR' => $color,
991             $field_name => $field_text . ' ' x $pad_size,
992             } ;
993             }
994             }
995              
996 0           $line->{NEW_LINE} ++ ;
997 0           push @lines, $line ;
998              
999 0           return @lines ;
1000             }
1001              
1002             #-------------------------------------------------------------------------------
1003              
1004             sub add_information
1005             {
1006              
1007             =head2 [P] add_information($split_data)
1008              
1009             Add information, according to the options passed to the constructor, to the internal data.
1010              
1011             I - See L
1012              
1013             =over 2
1014              
1015             =item * $split_data - data returned by _gather()
1016              
1017             =back
1018              
1019             I - Nothing
1020              
1021             I - None
1022              
1023             =cut
1024              
1025 0     0 0   my ($self, $split_data) = @_ ;
1026              
1027 0           unshift @{$split_data}, $self->get_information($split_data) ;
  0            
1028              
1029             }
1030              
1031             #-------------------------------------------------------------------------------
1032              
1033             sub get_information
1034             {
1035              
1036             =head2 [P] get_information($split_data)
1037              
1038             Returns information, according to the options passed to the constructor, to the internal data.
1039              
1040             I - See L
1041              
1042             =over 2
1043              
1044             =item * $split_data - data returned by _gather()
1045              
1046             =back
1047              
1048             I - Nothing
1049              
1050             I - None
1051              
1052             =cut
1053              
1054 0     0 0   my ($self, $split_data, $range_color) = @_ ;
1055 0   0       $range_color ||= '' ,
1056              
1057             my @information ;
1058              
1059 0 0         if($self->{DISPLAY_COLUMN_NAMES})
1060             {
1061 0           my $information = '' ;
1062            
1063 0           for my $field_name (@{$self->{FIELDS_TO_DISPLAY}})
  0            
1064             {
1065 0 0         if(exists $split_data->[0]{$field_name})
1066             {
1067 0   0       my $length = $self->{FIELD_LENGTH}{$field_name} || croak "Error: undefined field length" ;
1068            
1069 0           $information .= sprintf "%-${length}.${length}s ", $field_name
1070             }
1071             }
1072            
1073 0           push @information,
1074             {
1075             INFORMATION => [ {INFORMATION_COLOR => $range_color, INFORMATION => $information} ],
1076             NEW_LINE => 1,
1077             } ;
1078             }
1079              
1080 0 0         if($self->{DISPLAY_RULER})
1081             {
1082 0           my $information = '' ;
1083            
1084 0           for my $field_name (@{$self->{FIELDS_TO_DISPLAY}})
  0            
1085             {
1086 0 0         if(exists $split_data->[0]{$field_name})
1087             {
1088 0           for ($field_name)
1089             {
1090             /HEX_DUMP/ and do
1091 0 0         {
1092 0           $information .= $self->{OFFSET_FORMAT} =~ /x$/
1093 0           ? join '', map {sprintf '%x ' , $ _ % 16} (0 .. $self->{DATA_WIDTH} - 1)
1094 0 0         : join '', map {sprintf '%d ' , $ _ % 10} (0 .. $self->{DATA_WIDTH} - 1) ;
1095              
1096 0           $information .= ' ' ;
1097 0           last ;
1098             } ;
1099            
1100             /DEC_DUMP/ and do
1101 0 0         {
1102 0           $information .= $self->{OFFSET_FORMAT} =~ /x$/
1103 0           ? join '', map {sprintf '%x ' , $ _ % 16} (0 .. $self->{DATA_WIDTH} - 1)
1104 0 0         : join '', map {sprintf '%d ' , $ _ % 10} (0 .. $self->{DATA_WIDTH} - 1) ;
1105              
1106 0           $information .= ' ' ;
1107 0           last ;
1108             } ;
1109            
1110             /HEXASCII_DUMP/ and do
1111 0 0         {
1112 0           $information .= $self->{OFFSET_FORMAT} =~ /x$/
1113 0           ? join '', map {sprintf '%x ' , $ _ % 16} (0 .. $self->{DATA_WIDTH} - 1)
1114 0 0         : join '', map {sprintf '%d ' , $ _ % 10} (0 .. $self->{DATA_WIDTH} - 1) ;
1115 0           $information .= ' ' ;
1116 0           last ;
1117             } ;
1118            
1119             /ASCII_DUMP/ and do
1120 0 0         {
1121 0           $information .= $self->{OFFSET_FORMAT} =~ /x$/
1122 0           ? join '', map {sprintf '%x', $ _ % 16} (0 .. $self->{DATA_WIDTH} - 1)
1123 0 0         : join '', map {$ _ % 10} (0 .. $self->{DATA_WIDTH} - 1) ;
1124 0           $information .= ' ' ;
1125 0           last ;
1126             } ;
1127            
1128 0           $information .= ' ' x $self->{FIELD_LENGTH}{$field_name} . ' ' ;
1129             }
1130             }
1131             }
1132            
1133 0           push @information,
1134             {
1135             RULER => [ { RULER_COLOR => $range_color, RULER=> $information} ],
1136             NEW_LINE => 1,
1137             } ;
1138             }
1139            
1140 0           return @information ;
1141             }
1142              
1143             #-------------------------------------------------------------------------------
1144              
1145             1 ;
1146              
1147             =head1 BUGS AND LIMITATIONS
1148              
1149             None so far.
1150              
1151             =head1 AUTHOR
1152              
1153             Nadim ibn hamouda el Khemir
1154             CPAN ID: NKH
1155             mailto: nadim@cpan.org
1156              
1157             =head1 COPYRIGHT AND LICENSE
1158              
1159             Copyright Nadim Khemir 2010.
1160              
1161             This program is free software; you can redistribute it and/or
1162             modify it under the terms of either:
1163              
1164             =over 4
1165              
1166             =item * the GNU General Public License as published by the Free
1167             Software Foundation; either version 1, or (at your option) any
1168             later version, or
1169              
1170             =item * the Artistic License version 2.0.
1171              
1172             =back
1173              
1174             =head1 SUPPORT
1175              
1176             You can find documentation for this module with the perldoc command.
1177              
1178             perldoc Data::HexDump::Range
1179              
1180             You can also look for information at:
1181              
1182             =over 4
1183              
1184             =item * AnnoCPAN: Annotated CPAN documentation
1185              
1186             L
1187              
1188             =item * RT: CPAN's request tracker
1189              
1190             Please report any bugs or feature requests to L .
1191              
1192             We will be notified, and then you'll automatically be notified of progress on
1193             your bug as we make changes.
1194              
1195             =item * Search CPAN
1196              
1197             L
1198              
1199             =back
1200              
1201             =head1 SEE ALSO
1202              
1203             L
1204              
1205             =cut