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