]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/Eml.pm
eml: speed up common LF-only emails
[public-inbox.git] / lib / PublicInbox / Eml.pm
index 1adaff04f7be871d1ab9343c4bb737436b603d09..f022516c12cd091cd2ee6bc77212208f399b7522 100644 (file)
@@ -71,11 +71,19 @@ sub re_memo ($) {
 # compatible with our uses of Email::MIME
 sub new {
        my $ref = ref($_[1]) ? $_[1] : \(my $cpy = $_[1]);
-       if ($$ref =~ /(?:\r?\n(\r?\n))/gs) { # likely
-               # This can modify $$ref in-place and to avoid memcpy/memmove
-               # on a potentially large $$ref.  It does need to make a
-               # copy for $hdr, though.  Idea stolen from Email::Simple
-               my $hdr = substr($$ref, 0, pos($$ref), ''); # sv_chop on $$ref
+       # substr() can modify the first arg in-place and to avoid
+       # memcpy/memmove on a potentially large scalar.  It does need
+       # to make a copy for $hdr, though.  Idea stolen from Email::Simple.
+
+       # We also prefer index() on common LFLF emails since it's faster
+       # and re scan can bump RSS by length($$ref) on big strings
+       if (index($$ref, "\r\n") < 0 && (my $pos = index($$ref, "\n\n")) >= 0) {
+               # likely on *nix
+               my $hdr = substr($$ref, 0, $pos + 2, ''); # sv_chop on $$ref
+               chop($hdr); # lower SvCUR
+               bless { hdr => \$hdr, crlf => "\n", bdy => $ref }, __PACKAGE__;
+       } elsif ($$ref =~ /\r?\n(\r?\n)/s) {
+               my $hdr = substr($$ref, 0, $+[0], ''); # sv_chop on $$ref
                substr($hdr, -(length($1))) = ''; # lower SvCUR
                bless { hdr => \$hdr, crlf => $1, bdy => $ref }, __PACKAGE__;
        } elsif ($$ref =~ /^[a-z0-9-]+[ \t]*:/ims && $$ref =~ /(\r?\n)\z/s) {
@@ -90,8 +98,8 @@ sub new {
 sub new_sub {
        my (undef, $ref) = @_;
        # special case for messages like <85k5su9k59.fsf_-_@lola.goethe.zz>
-       $$ref =~ /\A(?:(\r?\n))/gs or goto &new;
-       my $hdr = substr($$ref, 0, pos($$ref), ''); # sv_chop on $$ref
+       $$ref =~ /\A(\r?\n)/s or goto &new;
+       my $hdr = substr($$ref, 0, $+[0], ''); # sv_chop on $$ref
        bless { hdr => \$hdr, crlf => $1, bdy => $ref }, __PACKAGE__;
 }
 
@@ -131,8 +139,12 @@ sub mp_descend ($$) {
        # Cut at the the first epilogue, not subsequent ones.
        # *sigh* just the regexp match alone seems to bump RSS by
        # length($$bdy) on a ~30M string:
-       $$bdy =~ /((?:\r?\n)?^--$bnd--[ \t]*\r?$)/gsm and
-               substr($$bdy, pos($$bdy) - length($1)) = '';
+       my $epilogue_missing;
+       if ($$bdy =~ /(?:\r?\n)?^--$bnd--[ \t]*\r?$/sm) {
+               substr($$bdy, $-[0]) = '';
+       } else {
+               $epilogue_missing = 1;
+       }
 
        # *Sigh* split() doesn't work in-place and return CoW strings
        # because Perl wants to "\0"-terminate strings.  So split()
@@ -150,6 +162,10 @@ sub mp_descend ($$) {
 
        if (@parts) { # the usual path if we got this far:
                undef $bdy; # release memory ASAP if $nr > 0
+
+               # compatibility with Email::MIME
+               $parts[-1] =~ s/\n\r?\n\z/\n/s if $epilogue_missing;
+
                @parts = grep /[^ \t\r\n]/s, @parts; # ignore empty parts
 
                # Keep "From: someone..." from preamble in old,