This is about as fast as you can go without changing your algorithm, as scanning character by character for the next/previous newlines is going to be necessary no matter what.
As your subroutine didn't do any error or out of bounds checking, or define what happens if C<$position> is on a line boundary already, I did not do anything with those edge cases. You probably will, though.
Full code is in the readmore.
use Benchmark qw/cmpthese/;
use Test::More;
use experimental 'signatures'; # for daxim
my $string ="test\nI want length of this line\n test";
my $position = 12; # Note this postion can be any position in any line
+.
# currently considering it inside 2nd line
if ($ENV{BENCHMARK}) {
cmpthese(-$ENV{BENCHMARK}, {
cur_strlen => sub { cur_strlen($string, $position) },
rlindex => sub { rlindex($string, $position) },
op => sub { op($string, $position) },
daxim => sub { daxim($string, $position) },
});
}
my @tests = (
[ "\nString\n", 2, 6, 'Surrounded' ],
[ "String\n", 2, 6, 'No first \n' ],
[ "\nString", 2, 6, 'No last \n' ],
[ "String", 2, 6, 'No \n' ],
[ "", 0, 0, 'Blank' ],
);
if ($ENV{TEST}) {
for (@tests) {
($string, $position, my $expected, my $desc) = @$_;
is cur_strlen($string, $position), $expected, "cur_strlen: $de
+sc";
is rlindex($string, $position), $expected, "rlindex: $desc"
+;
is op($string, $position), $expected, "op: $desc";
is daxim($string, $position), $expected, "daxim: $desc";
}
done_testing;
}
use Inline C => q@
int cur_strlen(char * str, int pos) {
int rindex = 0;
int lindex = 0;
for (rindex = pos; str[rindex] != '\n' && str[rindex]; rindex++);
for (lindex = pos; lindex && str[lindex] != '\n'; lindex--);
if (lindex == 0 && str[lindex] != '\n')
lindex--;
return(rindex - lindex - 1);
}
@;
sub rlindex {
my ($str, $pos) = @_;
my $right = index $str, "\n", $pos;
my $left = rindex $str, "\n", $pos;
$right = length $str if $right == -1;
$right - $left - 1;
}
sub daxim($s, $p, $n = "\n") {
no warnings 'uninitialized';
my ($prev, $next);
while ($s =~ /$n/g) {
$prev = $next;
$next = pos $s;
last if $next > $p;
}
return $next - $prev;
}
sub tybalt89 {
my ($string, $position) = @_;
pos($string) = $position;
$string =~ /.*\G.*\n?/;
my $length_of_line = length $&;
}
sub op {
my ($str, $pos) = @_;
die "str is not defined" unless defined $str;
die "pos is not defined" unless defined $pos;
my @newlines; # for storing \n positions
push @newlines, 0;
while ($str =~/\n/g) {
push @newlines, pos($str);
}
my $itr = scalar @newlines - 1;
while ($newlines[$itr] > $pos) {
$itr--;
}
my $length_of_line = $newlines[$itr + 1] - $newlines[$itr];
}