File Coverage

blib/lib/Set/SegmentTree/ValueLookup.pm
Criterion Covered Total %
statement 103 152 67.7
branch 38 78 48.7
condition 10 24 41.6
subroutine 12 16 75.0
pod 0 13 0.0
total 163 283 57.6


line stmt bran cond sub pod time code
1 1     1   491 use Set::SegmentTree::node;
  1         4  
  1         49  
2             package Set::SegmentTree::ValueLookup;
3             # table package auto-generated by Data::FlatTables
4 1     1   9 use strict;
  1         3  
  1         30  
5 1     1   16 use warnings;
  1         3  
  1         2680  
6              
7             sub new {
8 4     4 0 13 my ($class, %args) = @_;
9 4         10 my $self = bless {}, $class;
10              
11 4 100 66     19 $self->{root} = $args{root} if defined $args{root} and $args{root} != 0;
12             $self->{nodes} =
13             [ map {
14 58         176 Set::SegmentTree::node->new(%$_)
15 2         44 } @{$args{nodes}} ]
16 4 100       12 if exists $args{nodes};
17 4 100 66     18 $self->{created} = $args{created} if defined $args{created} and $args{created} != 0;
18              
19 4         11 return $self;
20             }
21              
22 0     0 0 0 sub flatbuffers_type { 'table' }
23              
24             my %basic_types = (
25             bool => { format => 'C', length => 1 },
26             byte => { format => 'c', length => 1 },
27             ubyte => { format => 'C', length => 1 },
28             short => { format => 's<', length => 2 },
29             ushort => { format => 'S<', length => 2 },
30             int => { format => 'l<', length => 4 },
31             uint => { format => 'L<', length => 4 },
32             float => { format => 'f<', length => 4 },
33             long => { format => 'q<', length => 8 },
34             ulong => { format => 'Q<', length => 8 },
35             double => { format => 'd<', length => 8 },
36             );
37 28 0 50 28 0 126 sub root { @_ > 1 ? $_[0]{root} = ( $_[1] == 0 ? undef : $_[1]) : $_[0]{root} // 0 }
    50          
38             sub nodes {
39             @_ > 1 ? $_[0]{nodes} =
40             [ map {
41 0 0 0     0 (ref and not UNIVERSAL::can($_, 'can')) ? Set::SegmentTree::node->new(%$_) : $_
42 0         0 } @{$_[1]} ]
43             : $_[0]{nodes}
44 264 50   264 0 768 }
45 0 0 0 0 0 0 sub created { @_ > 1 ? $_[0]{created} = ( $_[1] == 0 ? undef : $_[1]) : $_[0]{created} // 0 }
    0          
46              
47             sub deserialize {
48 2     2 0 9 my ($self, $data, $offset) = @_;
49 2   50     12 $offset //= 0;
50 2 50       8 $self = $self->new unless ref $self;
51              
52             # verify file identifier
53 2 50 33     12 if ($offset == 0 and 'RTRE' ne substr $data, 4, 4) {
54 0         0 die 'invalid fbs file identifier, "RTRE" expected';
55             }
56              
57 2         11 my $object_offset = $offset + unpack "L<", substr $data, $offset, 4;
58 2         7 my $vtable_offset = $object_offset - unpack "l<", substr $data, $object_offset, 4;
59 2         15 my @offsets = map unpack ("S<", $_), map substr ($data, $vtable_offset + $_ * 2, 2), 2 .. 4;
60              
61 2 50       10 $self->{root} = unpack 'l<', substr $data, $object_offset + $offsets[0], 4 if $offsets[0] != 0;
62 2 50       8 $self->{nodes} = $self->deserialize_array('[Set::SegmentTree::node]', $data, $object_offset + $offsets[1]) if $offsets[1] != 0;
63 2 50       8 $self->{created} = unpack 'l<', substr $data, $object_offset + $offsets[2], 4 if $offsets[2] != 0;
64              
65 2         26 return $self
66             }
67              
68              
69              
70             sub deserialize_string {
71 0     0 0 0 my ($self, $data, $offset) = @_;
72              
73 0         0 my $string_offset = $offset + unpack "L<", substr $data, $offset, 4; # dereference the string pointer
74 0         0 my $string_length = unpack "L<", substr $data, $string_offset, 4; # get the length
75 0         0 return substr $data, $string_offset + 4, $string_length # return a substring
76             }
77              
78             sub deserialize_array {
79 2     2 0 6 my ($self, $array_type, $data, $offset) = @_;
80              
81 2         13 $array_type = $array_type =~ s/\A\[(.*)\]\Z/$1/sr;
82              
83 2         5 $offset = $offset + unpack "L<", substr $data, $offset, 4; # dereference the array pointer
84 2         6 my $array_length = unpack "L<", substr $data, $offset, 4; # get the length
85 2         3 $offset += 4;
86              
87 2         3 my @array;
88 2 50       11 if (exists $basic_types{$array_type}) { # if its an array of numerics
    50          
    50          
89 0         0 @array = map { unpack $basic_types{$array_type}{format}, $_ }
90 0         0 map { substr $data, $offset + $_, $basic_types{$array_type}{length} }
91             map $_ * $basic_types{$array_type}{length},
92 0         0 0 .. ($array_length - 1);
93            
94             } elsif ($array_type eq "string") { # if its an array of strings
95 0         0 @array = map { $self->deserialize_string($data, $offset + $_) }
  0         0  
96             map $_ * 4,
97             0 .. ($array_length - 1);
98              
99             } elsif ($array_type =~ /\A\[/) { # if its an array of strings
100 0         0 @array = map { $self->deserialize_array($array_type, $data, $offset + $_) }
  0         0  
101             map $_ * 4,
102             0 .. ($array_length - 1);
103            
104             } else { # if its an array of objects
105 2 50       9 if ($array_type->flatbuffers_type eq "table") {
    0          
106 2         14 @array = map { $array_type->deserialize($data, $offset + $_) }
  58         140  
107             map $_ * 4,
108             0 .. ($array_length - 1);
109             } elsif ($array_type->flatbuffers_type eq "struct") {
110 0         0 my $length = $array_type->flatbuffers_struct_length;
111 0         0 @array = map { $array_type->deserialize($data, $offset + $_) }
  0         0  
112             map $_ * $length,
113             0 .. ($array_length - 1);
114             } else {
115             ...
116 0         0 }
117             }
118              
119             return \@array
120 2         8 }
121              
122              
123             sub serialize {
124 4     4 0 8 my ($self, $cache) = @_;
125 4 100       10 if (not defined $cache) {
126 2         3 $cache = {};
127              
128 2         15 my @objects = $self->serialize($cache);
129 2         8 my $root = $objects[0]; # get the root data structure
130              
131             # insert file identifier
132 2         8 unshift @objects, { type => 'file_identifier', data => 'RTRE' };
133              
134             # header pointer to root data structure
135 2         11 unshift @objects, { type => "header", data => "\0\0\0\0", reloc => [{ offset => 0, item => $root, type => "unsigned delta" }] };
136              
137 2         7 return $self->serialize_objects(@objects);
138             } else {
139              
140             my $vtable = $self->serialize_vtable(
141             defined $self->{root} ? 4 : 0,
142             defined $self->{nodes} ? 4 : 0,
143 2 50       13 defined $self->{created} ? 4 : 0,
    50          
    50          
144             );
145 2         4 my $data = "\0\0\0\0";
146              
147 2         7 my @reloc = ({ offset => 0, item => $vtable, type => "signed negative delta" });
148             # flatbuffers vtable offset is stored in negative form
149 2         4 my @objects = ($vtable);
150              
151 2 50       7 if (defined $self->{root}) {
152 2         6 $data .= pack 'l<', $self->{root};
153             }
154              
155 2 50       7 if (defined $self->{nodes}) {
156 2         6 my ($array_object, @array_objects) = $self->serialize_array('[Set::SegmentTree::node]', $self->{nodes}, $cache);
157 2         15 push @objects, $array_object, @array_objects;
158 2         14 push @reloc, { offset => length ($data), item => $array_object, type => 'unsigned delta'};
159 2         7 $data .= "\0\0\0\0";
160             }
161              
162 2 50       6 if (defined $self->{created}) {
163 2         5 $data .= pack 'l<', $self->{created};
164             }
165              
166             # pad to 4 byte boundary
167 2 50       5 $data .= pack "x" x (4 - (length ($data) % 4)) if length ($data) % 4;
168              
169             # return table data and other objects that we've created
170 2         17 return { type => "table", data => $data, reloc => \@reloc }, @objects
171             }
172             }
173            
174              
175             sub serialize_objects {
176 2     2 0 9 my ($self, @objects) = @_;
177              
178              
179 2         4 my $data = "";
180 2         3 my $offset = 0;
181              
182             # concatentate the data
183 2         5 for my $object (@objects) {
184 204         290 $object->{serialized_offset} = $offset;
185 204         290 $data .= $object->{data};
186 204         296 $offset += length $object->{data};
187             }
188              
189             # second pass for writing offsets to other parts
190 2         4 for my $object (@objects) {
191 204 100       400 if (defined $object->{reloc}) {
192             # perform address relocation
193 94         122 for my $reloc (@{$object->{reloc}}) {
  94         155  
194 200         252 my $value;
195 200 50 66     825 if (defined $reloc->{lambda}) { # allow the reloc to have a custom format
    100 33        
    50          
196 0         0 $value = $reloc->{lambda}($object, $reloc);
197             } elsif (defined $reloc->{type} and $reloc->{type} eq "unsigned delta") {
198 140         285 $value = pack "L<", $reloc->{item}{serialized_offset} - $object->{serialized_offset} - $reloc->{offset};
199             } elsif (defined $reloc->{type} and $reloc->{type} eq "signed negative delta") {
200 60         119 $value = pack "l<", $object->{serialized_offset} + $reloc->{offset} - $reloc->{item}{serialized_offset};
201             } else {
202             ...
203 0         0 }
204 200         377 substr $data, $object->{serialized_offset} + $reloc->{offset}, length($value), $value;
205             }
206             }
207             }
208              
209             # done, the data is now ready to be deserialized
210 2         136 return $data
211             }
212              
213             sub serialize_vtable {
214 2     2 0 7 my ($self, @lengths) = @_;
215              
216 2         3 my $offset = 4;
217 2         4 my @table;
218              
219 2         4 for (@lengths) { # parse table offsets
220 6 50       23 push @table, $_ ? $offset : 0;
221 6         21 $offset += $_;
222             }
223              
224 2         5 unshift @table, $offset; # prefix data length
225 2         5 unshift @table, 2 * (@table + 1); #prefix vtable length
226 2 50       7 push @table, 0 if @table % 2; # pad if odd count
227             # compile object
228 2         16 return { type => "vtable", data => pack "S<" x @table, @table }
229             }
230              
231             sub serialize_string {
232 0     0 0 0 my ($self, $string) = @_;
233              
234 0         0 my $len = pack "L<", length $string;
235 0         0 $string .= "\0"; # null termination byte because why the fuck not (it's part of flatbuffers)
236              
237 0         0 my $data = "$len$string";
238 0 0       0 $data .= pack "x" x (4 - (length ($data) % 4)) if length ($data) % 4; # pad to 4 byte boundary
239              
240 0         0 return { type => "string", data => $data }
241             }
242              
243              
244             sub serialize_array {
245 2     2 0 6 my ($self, $array_type, $array, $cache) = @_;
246              
247 2         15 $array_type = $array_type =~ s/\A\[(.*)\]\Z/$1/sr;
248              
249 2         7 my $data = pack "L<", scalar @$array;
250 2         3 my @array_objects;
251             my @reloc;
252              
253 2 50       13 if (exists $basic_types{$array_type}) { # array of scalar values
    50          
    50          
254 0         0 $data .= join "", map { pack $basic_types{$array_type}{format}, $_ } @$array;
  0         0  
255              
256             } elsif ($array_type eq "string") { # array of strings
257 0         0 $data .= "\0\0\0\0" x @$array;
258 0         0 for my $i (0 .. $#$array) {
259 0         0 my $string_object = $self->serialize_string($array->[$i]);
260 0         0 push @array_objects, $string_object;
261 0         0 push @reloc, { offset => 4 + $i * 4, item => $string_object, type => "unsigned delta" };
262             }
263             } elsif ($array_type =~ /\A\[/) { # array of arrays
264 0         0 $data .= "\0\0\0\0" x @$array;
265 0         0 for my $i (0 .. $#$array) {
266 0         0 my ($array_object, @child_array_objects) = $self->serialize_array($array_type, $array->[$i], $cache);
267 0         0 push @array_objects, $array_object, @child_array_objects;
268 0         0 push @reloc, { offset => 4 + $i * 4, item => $array_object, type => "unsigned delta" };
269             }
270              
271             } else { # else an array of objects
272 2 50       7 if ($array_type->flatbuffers_type eq "table") {
    0          
273 2         6 $data .= "\0\0\0\0" x @$array;
274 2         7 for my $i (0 .. $#$array) {
275 58         141 my ($root_object, @table_objects) = $array->[$i]->serialize($cache);
276 58         108 push @array_objects, $root_object, @table_objects;
277 58         183 push @reloc, { offset => 4 + $i * 4, item => $root_object, type => "unsigned delta" };
278             }
279             } elsif ($array_type->flatbuffers_type eq "struct") {
280 0         0 for my $i (0 .. $#$array) {
281 0         0 my ($root_object, @struct_objects) = $array->[$i]->serialize($cache);
282 0         0 push @array_objects, @struct_objects;
283 0         0 push @reloc, map { $_->{offset} += length ($data); $_ } @{$root_object->{reloc}};
  0         0  
  0         0  
  0         0  
284 0         0 $data .= $root_object->{data};
285              
286             }
287             } else {
288             ...
289 0         0 }
290             }
291              
292 2         23 return { type => "array", data => $data, reloc => \@reloc }, @array_objects
293             }
294              
295              
296              
297             1 # true return from package
298