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 )