App-Sandy
view release on metacpan or search on metacpan
lib/App/Sandy/PieceTable.pm view on Meta::CPAN
my $offset_acm = 0;
# Insert each piece reference into a tree
for my $piece ($self->_all_pieces) {
# Update piece offset
$piece->{offset} = $offset_acm;
# Update offset acumulator
$offset_acm += $piece->{len};
}
# Update logical offset with the corrected length
$self->logical_len($offset_acm);
}
sub lookup {
# Run 'calculate_logical_offset' before
my ($self, $start, $len) = @_;
state $comm = sub {
my ($pos, $piece) = @_;
if ($self->_is_pos_inside_range($pos, $piece->{offset}, $piece->{len})) {
return 0;
} elsif ($pos > $piece->{offset}) {
return 1;
} else {
return -1;
}
};
my $index = $self->with_bsearch($start, $self->piece_table,
$self->_count_pieces, $comm);
if (not defined $index) {
croak "Not found index for range: start = $start, length = $len";
}
my $piece = $self->_get_piece($index);
my @pieces;
do {
push @pieces => $piece;
$piece = $self->_get_piece(++$index);
} while ($piece && $self->_do_overlap($start, $len, $piece->{offset}, $piece->{len}));
return \@pieces;
}
sub _do_overlap {
my ($self, $start1, $len1, $start2, $len2) = @_;
my ($end1, $end2) = ($start1 + $len1 - 1, $start2 + $len2 - 1);
return $start1 <= $end2 && $start2 <= $end1;
}
sub _split_piece {
my ($self, $pos) = @_;
# Catch orig index where pos is inside
my $index = $self->_piece_at($pos);
# Get piece which will be updated
my $old_piece = $self->_get_piece($index);
# Split at the beggining of a piece,
# or this piece has length 1
if ($pos == $old_piece->{start}) {
return $index;
}
# Calculate piece end
my $old_end = $old_piece->{start} + $old_piece->{len} - 1;
# Calculate the corrected length according to the split
my $new_len = $pos - $old_piece->{start};
# Update piece
$old_piece->{len} = $new_len;
# Create the second part of the split after the break position
my $piece = $self->_piece_new($old_piece->{ref}, $old_piece->{is_orig},
$pos, $old_end - $pos + 1, $pos);
# Insert second part after updated piece
$self->_splice_piece(++$index, 0, $piece);
# return corrected index that resolves to
# the position between the breaked piece
return $index;
}
sub _is_pos_inside_range {
my ($self, $pos, $start, $len) = @_;
my $end = $start + $len - 1;
return $pos >= $start && $pos <= $end;
}
sub _piece_at {
my ($self, $pos) = @_;
# State the function to compare at bsearch
state $comm = sub {
my ($pos, $piece) = @_;
if ($self->_is_pos_inside_range($pos, $piece->{pos}, $piece->{len})) {
return 0;
} elsif ($pos > $piece->{pos}) {
return 1;
} else {
return -1;
}
};
# Search the piece index where $pos is inside the boundaries
my $index = $self->with_bsearch($pos, $self->piece_table,
$self->_count_pieces, $comm);
# Maybe it is undef. I need to take care to not
# search to a position that was removed before.
# I can avoid it when parsing the snv file
if (not defined $index) {
croak "Not found pos = $pos into piece_table. Maybe the region was removed?";
}
# Catch the piece at index
my $piece = $self->_get_piece($index);
# It needs to catch a orig piece, so if the piece
# is an insertion, it will search forward and backward
# from the actual index
if (not $piece->{is_orig}) {
my $new_index;
# Search forward
for (my $i = $index + 1; $i < $self->_count_pieces; $i++) {
my $piece = $self->_get_piece($i);
if ($piece->{is_orig}) {
if ($self->_is_pos_inside_range($pos, $piece->{pos}, $piece->{len})) {
$new_index = $i;
}
last;
}
}
( run in 0.855 second using v1.01-cache-2.11-cpan-39bf76dae61 )