blob: 54fdc54123dd05e9ae7bb2ae1b0e173880630ee0 [file] [log] [blame] [raw]
package RecvUntil;
use strict;
use warnings;
sub recv_until {
my ($pat) = @_;
my $len = length $pat;
my @backtracks;
for (my $i = 1; $i <= $len - 1; $i++) {
my $matched_prefix_len = 1;
while ($matched_prefix_len <= $len - $i - 1) {
#while (1) {
#my $left = $len - $i;
#warn "left: $i: $len: ", $len - 1 - $i, "\n";
#warn "matched_prefix_len: $matched_prefix_len\n";
#while (1) {
my $prefix = substr($pat, 0, $matched_prefix_len);
my $next = substr($pat, $matched_prefix_len, 1);
my $prefix2 = substr($pat, $i, $matched_prefix_len);
my $next2 = substr($pat, $i + $matched_prefix_len, 1);
#warn "$i: global prefix $prefix $next\n";
#warn "$i: local prefix $prefix2 $next2\n";
if ($prefix2 eq $prefix) {
if ($next2 eq $next) {
$matched_prefix_len++;
next;
}
#warn "$matched_prefix_len: $prefix: found match at $i (next $next, next2 $next2)\n";
my $cur_state = $i + $matched_prefix_len;
my $new_state = $matched_prefix_len + 1;
my $matched = substr($pat, 0, $cur_state);
my $chain = $backtracks[$cur_state - 2];
if (!$chain) {
$chain = [];
$backtracks[$cur_state - 2] = $chain;
}
my $found = 0;
for my $rec (@$chain) {
if ($rec->{char} eq $next) {
$found = 1;
if ($rec->{new_state} < $new_state) {
warn "overriding...\n";
$rec->{new_state} = $new_state;
}
}
}
if (!$found) {
warn "on state $cur_state ($matched), if next is '$next', ",
"then backtrack to state $new_state ($prefix$next)\n";
push @$chain, { char => $next, new_state => $new_state };
}
#if ($matched_prefix_len > 1) {
#$i += $matched_prefix_len - 1;
#}
last;
}
last;
}
}
return sub {
my ($txt) = @_;
my $max_state = length $pat;
my $len = length $txt;
my $state = 0;
my $ret = '';
for (my $i = 0; $i < $len; $i++) {
# read the char
my $c = substr($txt, $i, 1);
#warn "$state: read char at $i: $c\n";
#warn "matched: $ret\n";
my $expected = substr($pat, $state, 1);
if ($expected eq $c) {
#warn "matched the char in pattern.\n";
$state++;
if ($state == $max_state) {
last;
}
next;
}
if ($state == 0) {
#warn "did not match the first char in pattern\n";
$ret .= $c;
next;
}
my $old_state;
my $matched;
my $chain = $backtracks[$state - 2];
for my $rec (@$chain) {
if ($rec->{char} eq $c) {
$old_state = $state;
$state = $rec->{new_state};
#warn "matched the char for backtracking to state $state\n";
$matched = 1;
last;
}
}
if (!$matched) {
$ret .= substr($pat, 0, $state);
$state = 0;
redo;
}
$ret .= substr($pat, 0, $old_state + 1 - $state);
next;
}
return $ret;
};
}
1;