| 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; |