File Coverage

blib/lib/Net/OSCAR/TLV.pm
Criterion Covered Total %
statement 24 73 32.8
branch 2 14 14.2
condition 2 12 16.6
subroutine 6 15 40.0
pod 0 4 0.0
total 34 118 28.8


line stmt bran cond sub pod time code
1             =pod
2              
3             =head1 NAME
4              
5             Net::OSCAR::TLV -- tied hash for OSCAR TLVs
6              
7             =head1 VERSION
8              
9             version 1.928
10              
11             =head1 DESCRIPTION
12              
13             Keys in hashes tied to this class will be treated as numbers.
14             This class also preserves the ordering of its keys.
15              
16             =cut
17              
18             package Net::OSCAR::TLV;
19             BEGIN {
20 5     5   148 $Net::OSCAR::TLV::VERSION = '1.928';
21             }
22              
23             $REVISION = '$Revision$';
24              
25 5     5   24 use strict;
  5         8  
  5         154  
26 5     5   21 use vars qw(@EXPORT @ISA);
  5         9  
  5         4894  
27              
28             require Exporter;
29             @ISA = qw(Exporter);
30             @EXPORT = qw(tlv);
31              
32             # Extra arguments: an optional scalar which modifies the behavior of $self->{foo}->{bar} = "baz"
33             # Iff foo doesn't exist, the scalar will be evaluated and assigned as the value of foo.
34             # So, instead of having foo be {bar => "baz"} , it could be another TLV.
35             # It will be given the key bar.
36             sub new {
37 0     0 0 0 my $pkg = shift;
38 0         0 my $self = $pkg->TIEHASH(@_);
39             }
40              
41              
42             sub getorder {
43 0     0 0 0 my $self = shift;
44 0         0 return map { (unpack("n", $_))[0] } @{$self->{ORDER}};
  0         0  
  0         0  
45             }
46              
47             sub setorder {
48 0     0 0 0 my $self = shift;
49              
50             # Anything not specified gets shoved at the end
51 0         0 my @end = grep { my $inbud = $_; not grep { $_ eq $inbud } @_ } @{$self->{ORDER}};
  0         0  
  0         0  
  0         0  
  0         0  
52              
53 0         0 @{$self->{ORDER}} = map { pack("n", 0+$_) } @_;
  0         0  
  0         0  
54 0         0 push @{$self->{ORDER}}, @end;
  0         0  
55             }
56              
57             sub TIEHASH {
58 5     5   14 my $class = shift;
59 5         26 my $self = { DATA => {}, ORDER => [], CURRKEY => -1, AUTOVIVIFY => shift};
60 5         27 return bless $self, $class;
61             }
62              
63             sub FETCH {
64 0     0   0 my($self, $key) = @_;
65 0         0 $self->{DATA}->{pack("n", 0+$key)};
66             }
67              
68             sub STORE {
69 105     105   148 my($self, $key, $value) = @_;
70 105         191 my($normalkey) = pack("n", 0+$key);
71              
72             #print STDERR "Storing: ", Data::Dumper->Dump([$value], ["${self}->{$key}"]);
73 105 50       261 if(!exists $self->{DATA}->{$normalkey}) {
74 105 50 33     337 if(
      33        
      0        
75             $self->{AUTOVIVIFY} and
76             ref($value) eq "HASH" and
77             !tied(%$value) and
78             scalar keys %$value == 0
79             ) {
80             #print STDERR "Autovivifying $key: $self->{AUTOVIVIFY}\n";
81 0         0 eval $self->{AUTOVIVIFY};
82             #print STDERR "New value: ", Data::Dumper->Dump([$self->{DATA}->{$normalkey}], ["${self}->{$key}"]);
83             } else {
84             #print STDERR "Not autovivifying $key.\n";
85             #print STDERR "No autovivify.\n" unless $self->{AUTOVIVIFY};
86             #printf STDERR "ref(\$value) eq %s\n", ref($value) unless ref($value) eq "HASH";
87             #print STDERR "tied(\%\$value)\n" unless !tied(%$value);
88             #printf STDERR "scalar keys \%\$value == %d\n", scalar keys %$value unless scalar keys %$value == 0;
89             }
90 105         111 push @{$self->{ORDER}}, $normalkey;
  105         236  
91             } else {
92             #print STDERR "Not autovivifying $key: already exists\n";
93             }
94 105         271 $self->{DATA}->{$normalkey} = $value;
95 105         379 return $value;
96             }
97              
98             sub DELETE {
99 0     0   0 my($self, $key) = @_;
100 0         0 my($packedkey) = pack("n", 0+$key);
101 0         0 delete $self->{DATA}->{$packedkey};
102 0         0 for(my $i = 0; $i < scalar @{$self->{ORDER}}; $i++) {
  0         0  
103 0 0       0 next unless $packedkey eq $self->{ORDER}->[$i];
104 0         0 splice(@{$self->{ORDER}}, $i, 1);
  0         0  
105              
106             # What if the user deletes a key while iterating? We need to correct for the new index.
107 0 0 0     0 if($self->{CURRKEY} != -1 and $i <= $self->{CURRKEY}) {
108 0         0 $self->{CURRKEY}--;
109             }
110              
111 0         0 last;
112             }
113             }
114              
115             sub CLEAR {
116 0     0   0 my $self = shift;
117 0         0 $self->{DATA} = {};
118 0         0 $self->{ORDER} = [];
119 0         0 $self->{CURRKEY} = -1;
120 0         0 return $self;
121             }
122              
123             sub EXISTS {
124 0     0   0 my($self, $key) = @_;
125 0         0 my($packedkey) = pack("n", 0+$key);
126 0         0 return exists $self->{DATA}->{$packedkey};
127             }
128              
129             sub FIRSTKEY {
130 0     0   0 $_[0]->{CURRKEY} = -1;
131 0         0 goto &NEXTKEY;
132             }
133              
134             sub NEXTKEY {
135 0     0   0 my ($self) = @_;
136              
137 0         0 my $currkey = ++$self->{CURRKEY};
138 0 0       0 if($currkey >= scalar @{$self->{ORDER}}) {
  0         0  
139 0 0       0 return wantarray ? () : undef;
140             }
141              
142 0         0 my $packedkey = $self->{ORDER}->[$currkey];
143 0         0 my($key) = unpack("n", $packedkey);
144 0 0       0 return wantarray ? ($key, $self->{DATA}->{$packedkey}) : $key;
145             }
146              
147              
148             sub tlv(;@) {
149 5     5 0 18 my %tlv = ();
150 5         39 tie %tlv, "Net::OSCAR::TLV";
151 5         30 while(@_) { my($key, $value) = (shift, shift); $tlv{$key} = $value; }
  105         136  
  105         322  
152 5         529 return \%tlv;
153             }
154              
155              
156             1;