Crypt-SecretBuffer

 view release on metacpan or  search on metacpan

t/11-index.t  view on Meta::CPAN

            [ MATCH_NEGATE | MATCH_MULTI, 'abc', 0,16 ],
            [ MATCH_NEGATE | MATCH_MULTI, 'babababa', 0,1 ],
            [ MATCH_NEGATE | MATCH_MULTI, 'bababababababababa', 0,27 ],
            [ MATCH_NEGATE | MATCH_MULTI | MATCH_ANCHORED, 'ab' ],
            [ MATCH_NEGATE | MATCH_MULTI | MATCH_ANCHORED, 'abc', 0,16 ],
            [ MATCH_NEGATE | MATCH_MULTI | MATCH_ANCHORED, 'babababa', 0,1 ],
         ) {
            my ($flags, $pattern, $pos, $len)= @$_;
            $flags |= $const_time | $reverse;
            my $name= ($flags & MATCH_ANCHORED? 'ANCHORED ':'')
                    . ($flags & MATCH_MULTI? 'MULTI ':'')
                    . ($flags & MATCH_NEGATE? 'NEGATE ':'')
                    . $pattern;
            if ($reverse) {
               $pattern= reverse $pattern;
               $pos= length($str) - ($pos + $len)
                  if defined $pos;
            }
            my $expect= defined $pos? "$pos+$len" : '';
            is( join('+',$buf->scan($pattern, $flags)), $expect, $name );
         }
      };
   }
}

# Test that the CONST_TIME flag yields a full scan of the buffer regardless
# of where the match occurs.
subtest scan_const_time => sub {
   plan skip_all => "Set SB_TEST_CONSTTIME to enable this test"
      unless $ENV{SB_TEST_CONSTTIME};
   
   my $long= secret(("x" x 40_000) . ('z'x100));
   my $bench= cmpthese(-2, {
         find_early        => sub { $long->scan(('x'x100), MATCH_CONST_TIME) },
         find_late         => sub { $long->scan(('z'x100), MATCH_CONST_TIME) },
         not_found         => sub { $long->scan(('a'x100), MATCH_CONST_TIME) },
         find_negate_early => sub { $long->scan(('z'x100), MATCH_CONST_TIME|MATCH_NEGATE) },
         find_negate_late  => sub { $long->scan(('x'x100), MATCH_CONST_TIME|MATCH_NEGATE) },
      });
   my @keys= qw( find_early find_late not_found find_negate_early find_negate_late );
   my @rate= map { $_->iters / $_->cpu_a } @{$bench}{@keys};
   my $max= max @rate;
   is( $rate[$_] / $max, float(1, tolerance => .2), "$keys[$_] matches slowest method" )
      for 0..$#keys;
};

sub _render_char {
   $_[0] >= 0x21 && $_[0] <= 0x7E? chr $_[0] : sprintf("\\x%02X", $_[0])
}
sub bitmap_to_invlist {
   my @invlist;
   for (0..0xFF) {
      push @invlist, $_ if vec($_[0], $_, 1) ^ (@invlist & 1);
   }
   return \@invlist
}

# Test the inversion lists created for various charsets.
# Right now this is converting bitmaps from first 256 bytes into an inversion list,
# but in the future I'd like the back-end to be using inversion lists and able to cover
# unicode.
subtest charset => sub {
   # tests below use \x{100} to force perl-interpretation of a regex
   # as a baseline to compare the parsed bitmap to the perl-generated one.
   my $uni_literal= "\x{1000}";
   # third column regards unicode above 0x7F: 0 = none match, 1 = all match, 2 = need to test
   my @tests= (
      [ qr/[a-z]/                      => [97, 123], 0 ],
      [ qr/[a-z]/i                     => [65, 91, 97, 123], 0 ],
      ($] >= '5.026'? ( # /xx wasn't added until 5.26
         [ qr/[a-z 5\x{100}]/ixx       => [53, 54, 65, 91, 97, 123], 2 ],
         [ qr/[a-z 5]/ixx              => [53, 54, 65, 91, 97, 123], 0 ],
      ):()),
      [ do { no warnings; qr/[\0-\108\7777-9]/ } => [0, 9, 55, 58], 2 ],
      [ qr/[\t\r\n]/                   => [9, 11, 13, 14], 0 ],
      [ qr/[[:alpha:]]/                => [65, 91, 97, 123], 2 ],
      [ qr/[\x00-\e]/                  => [0, 28], 0 ],
      [ qr/[$uni_literal]/             => [ 0x1000, 0x1001 ], 2 ],
      [ qr/[\p{Block: Katakana}]/      => [ 0x30A0, 0x3100 ], 2 ],
      [ qr/[^[:digit:]]/               => [ 0,0x30, 0x3A ], 2 ],
      ($] >= '5.012'? ( # \p{digit} wasn't available until 5.12
         [ qr/[[:alpha:]\P{digit}]/    => [ 0,0x30, 0x3A ], 2 ],
      ):()),
      [ qr/[\p{alpha}\P{alpha}]/       => [ 0 ], 2 ],
      [ qr/[^\0\n]/                    => [ 1,10, 11 ], 1 ],
      [ qr/./                          => [ 0,10, 11 ], 1 ],
      [ qr/./s                         => [ 0 ], 1 ],
      [ qr/\w/                         => [ 48, 58, 65, 91, 95, 96, 97, 123 ], 2 ],
      [ qr/\W/                         => [ 0, 48, 58, 65, 91, 95, 96, 97, 123 ], 2 ],
      [ qr/\d/                         => [ 48, 58 ], 2 ],
      [ qr/\D/                         => [ 0, 48, 58 ], 2 ],
      [ qr/\s/                         => [ 9, ($] < '5.018'? (11, 12):()), 14, 32, 33 ], 2 ],
      [ qr/\S/                         => [ 0, 9, ($] < '5.018'? (11, 12):()), 14, 32, 33 ], 2 ],
   );
   for (@tests) {
      my ($re, $invlist, $above7F)= @$_;
      my $cset= Crypt::SecretBuffer::Exports::_debug_charset($re);
      $cset->{invlist}= bitmap_to_invlist(delete $cset->{bitmap});
      # for now, remove all invlist items greater than 0xFF
      pop @{$cset->{invlist}} while 0xFF < ($cset->{invlist}[-1]||0);
      pop @$invlist while 0xFF < ($invlist->[-1]||0);
      is( $cset, { invlist => $invlist, unicode_above_7F => $above7F }, "$re" );
   }
};

subtest index_charset => sub {
   my $buf = Crypt::SecretBuffer->new("abc123\0abc456" );
   is( $buf->index(qr/[0-9]/), 3, 'find first digit' );
   is( $buf->rindex(qr/[0-9]/), 12, 'find last digit' );
   is( $buf->index(qr/[a-z]/), 0, 'find first alpha' );
   is( $buf->rindex(qr/[a-z]/), 9, 'find last alpha' );
};

subtest scan_charset => sub {
   my $str= "abc123\x{100}\x{1000}abc456";
   utf8::encode($str);
   my $buf = Crypt::SecretBuffer->new($str);
   is( [$buf->scan(qr/[0-9]/)], [3,1], 'find digit' );
   is( [$buf->scan(qr/[0-9]/, MATCH_MULTI)], [3,3], 'find span of digits' );
   is( [$buf->scan(qr/[^a-z0-9]/, UTF8)], [6, 2], 'single char of unicode spans 2 bytes' );
   is( [$buf->scan(qr/[^a-z0-9]+/, UTF8)], [6, 5], 'unicode spans 2+3 bytes' );
   is( [$buf->scan(qr/[^a-z0-9]/, UTF8|MATCH_REVERSE)], [8, 3], 'second char of unicode spans 3 bytes' );
   is( [$buf->scan(qr/[^a-z0-9]+/, UTF8|MATCH_REVERSE)], [6, 5], 'unicode spans 2+3 bytes' );
};

done_testing;



( run in 0.594 second using v1.01-cache-2.11-cpan-e1769b4cff6 )