File Coverage

blib/lib/Set/SegmentTree/node.pm
Criterion Covered Total %
statement 95 157 60.5
branch 52 110 47.2
condition 15 33 45.4
subroutine 14 18 77.7
pod 0 16 0.0
total 176 334 52.6


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