File Coverage

blib/lib/Class/Bits.pm
Criterion Covered Total %
statement 87 94 92.5
branch 24 34 70.5
condition 5 6 83.3
subroutine 13 14 92.8
pod 1 1 100.0
total 130 149 87.2


line stmt bran cond sub pod time code
1             package Class::Bits;
2              
3 1     1   26520 use 5.006;
  1         4  
  1         54  
4              
5             our $VERSION = '0.05';
6              
7             # use strict;
8 1     1   6 use warnings::register;
  1         2  
  1         177  
9 1     1   5 use warnings ();
  1         7  
  1         22  
10              
11 1     1   931 use integer;
  1         12  
  1         6  
12              
13             require Exporter;
14              
15             our @ISA = qw(Exporter);
16             our @EXPORT = qw(make_bits);
17              
18 1     1   73 use Carp;
  1         2  
  1         122  
19 1     1   7 use Config;
  1         1  
  1         57  
20              
21 1     1   5 use constant nvsize => $Config{nvsize}*8;
  1         2  
  1         976  
22              
23             my %umax = ( 1 => 1,
24             2 => 3,
25             4 => 15,
26             8 => 255,
27             16 => 65535,
28             32 => 4294967295 );
29              
30             my %smax = ( 1 => 0,
31             2 => 1,
32             4 => 7,
33             8 => 127,
34             16 => 32767,
35             32 => 2147483647 );
36              
37             my %smin = ( 1 => -1,
38             2 => -2,
39             4 => -8,
40             8 => -128,
41             16 => -32768,
42             32 => -2147483648 );
43              
44             my %sext = map { $_ => (~$smax{$_}) } keys(%smax);
45              
46             my %signed = ( 's' => 1,
47             'u' => 0,
48             '' => 0 );
49              
50             sub make_bits {
51 2 50   2 1 20 @_ & 1 and
52             croak 'Class::Bits::bits called with an even number of arguments';
53              
54 2         2 my %names;
55 2         3 my $offset=0;
56 2         7 my $pkg=caller();
57              
58 2         6 while(@_) {
59 7         10 my $name=shift;
60 7 50       19 exists $names{$name} and
61             croak "repeated name '$name'";
62 7         11 $names{$name}=1;
63              
64 7         8 my $spec=shift;
65 7 50       35 $spec=~/^\s*([us]?)\s*(\d+)\s*$/ or
66             croak "invalid Class::Bits specification '$spec' for '$name'";
67 7         27 my $sig=$signed{$1};
68 7         13 my $size=$2;
69              
70 7 50       16 exists $smax{$size} or
71             croak "invalid Class::Bits size '$size' for '$name'";
72              
73 7         14 my $index=int(($offset+$size-1)/$size);
74 7         8 $offset=($index+1)*$size;
75              
76 7         29 $pkg->{INDEX}{$name}=$index;
77 7         15 $pkg->{SIZE}{$name}=$size;
78 7         13 $pkg->{SIGNED}{$name}=$sig;
79              
80             # warn "$name: index=>$index, size=>$size, sig=>$sig";
81              
82 7 100       12 if ($sig) {
83 3         5 my $max=$smax{$size};
84 3         5 my $min=$smin{$size};
85 3         5 my $ext=$sext{$size};
86              
87 3         19 *{"${pkg}::$name"}=sub {
88 9     9   16 my $this=shift;
89 9 100       24 if (@_) {
90 7         8 my $value=shift;
91 7 100 66     30 if ($value > $max or $value < $min) {
92 3 50       285 warnings::warn "value $value for "
93             .ref($this)
94             ."::$name out of range [$min, $max]"
95             if warnings::enabled();
96             }
97 7         22 vec ($$this, $index, $size) = $value;
98             }
99 9         15 my $value=vec ($$this, $index, $size);
100 9 100       20 if ($value & $ext) {
101 5         23 return $ext|$value;
102             }
103 4         18 return $value;
104             }
105 3         12 }
106             else {
107              
108 4         9 my $max=$umax{$size};
109              
110 4         23 *{"${pkg}::$name"}=sub {
111 8     8   18 my $this=shift;
112 8 100       21 if (@_) {
113 6         9 my $value=shift;
114 6 50       11 if (!defined($value)) {
115 0         0 warnings::warnif('uninitialized',
116             "Uninitialized value passed to $name accessor");
117 0         0 $value=0;
118             }
119 6 100 100     272 warnings::warnif("value $value for ".ref($this)."::$name out of range [0, $max]")
120             if ($value > $max or $value < 0);
121 6         37 vec ($$this, $index, $size) = $value;
122             }
123             else {
124 2         672 vec ($$this, $index, $size);
125             }
126 4         17 };
127             }
128             }
129              
130 2         16 *{"${pkg}::new"}=sub {
131 4     4   15 my $ref=shift;
132 4         4 my ($class, $string);
133 4 50       10 if (ref($ref)) {
134 0         0 $class=ref($ref);
135 0         0 $string=$$ref;
136             }
137             else {
138 4         28 $class=$ref;
139 4         21 $string="\0" x ((7+ $offset) >> 3)
140             }
141            
142 4 100       12 $string=shift if @_ & 1;
143              
144 4         7 my $this=\$string;
145 4         8 bless $this, $class;
146              
147 4         11 my %opts=@_;
148 4         13 for my $k (keys %opts) {
149 3         10 $this->$k($opts{$k});
150             }
151            
152 4         15 return $this;
153 2         69 };
154              
155 2     1   9 *{"${pkg}::length"}=sub { $offset }
  1         12  
156 2 50       16 unless exists $names{lenght};
157              
158 2     1   8 *{"${pkg}::keys"}=sub { keys %names }
  1         11  
159 2 50       12 unless exists $names{keys};
160              
161 2         13 *{"${pkg}::as_hash"}=sub {
162 0     0     my $this=shift;
163 0           map { ($_, $this->$_ ) } keys %names
  0            
164             }
165 2 50       20 unless exists $names{as_hash};
166             }
167              
168              
169              
170             1;
171             __END__