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