File Coverage

lib/SMB/Packer.pm
Criterion Covered Total %
statement 82 89 92.1
branch 14 16 87.5
condition 13 27 48.1
subroutine 25 29 86.2
pod 24 25 96.0
total 158 186 84.9


line stmt bran cond sub pod time code
1             # SMB Perl library, Copyright (C) 2014 Mikhael Goikhman, migo@cpan.org
2             #
3             # This program is free software: you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by
5             # the Free Software Foundation, either version 3 of the License, or
6             # (at your option) any later version.
7             #
8             # This program is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11             # GNU General Public License for more details.
12             #
13             # You should have received a copy of the GNU General Public License
14             # along with this program. If not, see .
15              
16             package SMB::Packer;
17              
18 3     3   761 use strict;
  3         5  
  3         90  
19 3     3   15 use warnings;
  3         4  
  3         90  
20              
21 3     3   12 use bytes;
  3         4  
  3         18  
22 3     3   1026 use Encode 'encode';
  3         12173  
  3         4053  
23              
24             sub new ($$) {
25 3     3 1 427 my $class = shift;
26              
27 3         9 my $self = bless {}, $class;
28              
29 3         10 return $self->reset;
30             }
31              
32             sub reset ($) {
33 14     14 1 2257 my $self = shift;
34              
35 14         33 $self->{data} = '';
36 14         23 $self->{offset} = 0;
37 14         26 $self->{marks} = {};
38 14         31 $self->{stubs} = {};
39              
40 14         51 return $self;
41             }
42              
43 10     10 1 52 sub data { $_[0]->{data} }
44 23     23 1 1391 sub size { length($_[0]->{data}) }
45 19     19 1 87 sub offset { $_[0]->{offset} }
46              
47             sub zero ($$) {
48 1     1 1 2 my $self = shift;
49 1   50     5 my $n_bytes = shift // die;
50              
51 1         25 substr($self->{data}, $self->{offset}, $n_bytes) = "\0" x $n_bytes;
52              
53 1         13 return $self->skip($n_bytes);
54             }
55              
56             sub skip ($$) {
57 2     2 1 4 my $self = shift;
58 2   50     8 my $n_bytes = shift // die;
59              
60 2 100       7 my $n_avail = $self->{offset} + $n_bytes > $self->size
61             ? $self->size - $self->{offset} : $n_bytes;
62              
63 2 100       14 $self->zero($n_bytes - $n_avail) if $n_avail < $n_bytes;
64              
65 2         4 $self->{offset} += $n_avail;
66              
67 2         7 return $self;
68             }
69              
70             sub stub ($$$) {
71 3     3 1 7 my $self = shift;
72 3   50     12 my $name = shift // '';
73 3   50     8 my $type = shift // '';
74              
75 3 50 66     26 die "type must be either size of bytes or uint{8,16{,_be},32{,_be},64}"
76             unless $type =~ /^uint(8|16(_be)?|32(_be)?|64)$/ || $type =~ /^\d+$/;
77 3         14 $self->{stubs}{$name} = [ $self->{offset}, $type ];
78              
79 3 100       30 $type =~ /^\d+$/
80             ? $self->bytes("\0" x $type)
81             : $self->$type(0);
82              
83 3         13 return $self;
84             }
85              
86             sub fill ($$) {
87 3     3 1 8 my $self = shift;
88 3   50     10 my $name = shift // '';
89 3   50     8 my $data = shift // die;
90              
91 3         6 my $curr_offset = $self->{offset};
92 3 50       3 my ($offset, $type) = @{$self->{stubs}{$name} || die "No previously set stub '$name'"};
  3         15  
93 3         6 $self->{offset} = $offset;
94 3 100       18 $type =~ /^\d+$/
95             ? $self->bytes($data)
96             : $self->$type($data);
97 3         6 $self->{offset} = $curr_offset;
98              
99 3         12 return $self;
100             }
101              
102             sub mark ($$) {
103 1     1 1 3 my $self = shift;
104 1   50     5 my $name = shift // '';
105              
106 1         4 $self->{marks}{$name} = $self->{offset};
107              
108 1         4 return $self;
109             }
110              
111             sub jump ($$) {
112 1     1 1 3 my $self = shift;
113 1   50     5 my $name = shift // '';
114              
115 1   50     6 $self->{offset} = $self->{marks}{$name} || 0;
116              
117 1         4 return $self;
118             }
119              
120             sub diff ($$) {
121 0     0 1 0 my $self = shift;
122 0   0     0 my $name = shift // '';
123              
124 0   0     0 return $self->{offset} - ($self->{marks}{$name} || 0);
125             }
126              
127             my %UINT_MODS = (
128             +1 => 'C',
129             +2 => 'v',
130             +4 => 'V',
131             -1 => 'C',
132             -2 => 'n',
133             -4 => 'N',
134             );
135              
136             sub uint ($$$$) {
137 103     103 0 126 my $self = shift;
138 103         105 my $n_bytes = shift;
139 103 100       168 my $be_factor = shift() ? -1 : 1;
140 103         91 my $i = shift;
141              
142 103         364 return $self->bytes(pack($UINT_MODS{$be_factor * $n_bytes}, $i));
143             }
144              
145             sub str ($$;$) {
146 15     15 1 19 my $self = shift;
147 15         17 my $str = shift;
148 15   100     64 my $enc = shift || 'UTF-16LE';
149              
150 15         42 return $self->bytes(encode($enc, $str));
151             }
152              
153             sub bytes ($$) {
154 136     136 1 8286 my $self = shift;
155 136         149 my $data = shift;
156              
157 136 100       271 $data = join('', @$data) if ref($data) eq 'ARRAY';
158              
159 136         260 substr($self->{data}, $self->{offset}, length($data)) = $data;
160 136         186 $self->{offset} += length($data);
161              
162 136         554 return $self;
163             }
164              
165 14     14 1 4225 sub uint8 { uint($_[0], 1, 0, $_[1]); }
166 52     52 1 1516 sub uint16 { uint($_[0], 2, 0, $_[1]); }
167 28     28 1 560 sub uint32 { uint($_[0], 4, 0, $_[1]); }
168 6     6 1 1379 sub uint16_be { uint($_[0], 2, 1, $_[1]); }
169 3     3 1 471 sub uint32_be { uint($_[0], 4, 1, $_[1]); }
170 3     3 1 9 sub uint64 { uint32($_[0], $_[1] & 0xffffffff); uint32($_[0], $_[1] >> 32); }
  3         9  
171 1     1 1 5 sub utf16 { str($_[0], $_[1]); }
172 0     0 1   sub utf16_be { str($_[0], $_[1], 'UTF-16BE'); }
173 0     0 1   sub fid1 { uint16($_[0], $_[1]); }
174 0     0 1   sub fid2 { uint64($_[0], $_[1][0]); uint64($_[0], $_[1][1]); }
  0            
175              
176             1;
177              
178             __END__