line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Copyright 2009, 2010, 2011, 2014, 2018, 2019 Kevin Ryde. |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# This file is part of File-Locate-Iterator. |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# File-Locate-Iterator is free software; you can redistribute it and/or |
6
|
|
|
|
|
|
|
# modify it under the terms of the GNU General Public License as published |
7
|
|
|
|
|
|
|
# by the Free Software Foundation; either version 3, or (at your option) |
8
|
|
|
|
|
|
|
# any later version. |
9
|
|
|
|
|
|
|
# |
10
|
|
|
|
|
|
|
# File-Locate-Iterator is distributed in the hope that it will be useful, |
11
|
|
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
12
|
|
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
13
|
|
|
|
|
|
|
# Public License for more details. |
14
|
|
|
|
|
|
|
# |
15
|
|
|
|
|
|
|
# You should have received a copy of the GNU General Public License along |
16
|
|
|
|
|
|
|
# with File-Locate-Iterator; see the file COPYING. Failing that, go to |
17
|
|
|
|
|
|
|
# . |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
package File::Locate::Iterator::FileMap; |
20
|
4
|
|
|
4
|
|
7164
|
use 5.006; |
|
4
|
|
|
|
|
13
|
|
21
|
4
|
|
|
4
|
|
21
|
use strict; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
91
|
|
22
|
4
|
|
|
4
|
|
19
|
use warnings; |
|
4
|
|
|
|
|
13
|
|
|
4
|
|
|
|
|
98
|
|
23
|
4
|
|
|
4
|
|
18
|
use Carp; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
1938
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# uncomment this to run the ### lines |
26
|
|
|
|
|
|
|
#use Devel::Comments; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
our $VERSION = 28; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
our %cache; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub _key { |
33
|
43
|
|
|
43
|
|
73
|
my ($fh) = @_; |
34
|
43
|
|
|
|
|
422
|
my ($dev, $ino, undef, undef, undef, undef, undef, $size) = stat ($fh); |
35
|
43
|
|
|
|
|
276
|
return "$dev,$ino,$size,".tell($fh); |
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
sub find { |
38
|
9
|
|
|
9
|
0
|
20
|
my ($class, $fh) = @_; |
39
|
9
|
|
|
|
|
24
|
return $cache{_key($fh)}; |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# return a FileMap object which is $fh mmapped |
43
|
|
|
|
|
|
|
sub get { |
44
|
34
|
|
|
34
|
0
|
88
|
my ($class, $fh) = @_; |
45
|
|
|
|
|
|
|
|
46
|
34
|
|
|
|
|
69
|
my $key = _key($fh); |
47
|
|
|
|
|
|
|
### cache get: "$fh, $key, size=".(-s $fh) |
48
|
34
|
|
66
|
|
|
137
|
return ($cache{$key} || do { |
49
|
|
|
|
|
|
|
require File::Map; |
50
|
|
|
|
|
|
|
# File::Map->VERSION('0.35'); # for binary handled properly, maybe |
51
|
|
|
|
|
|
|
File::Map->VERSION('0.38'); # for tainting |
52
|
|
|
|
|
|
|
require PerlIO::Layers; |
53
|
|
|
|
|
|
|
require Scalar::Util; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
PerlIO::Layers::query_handle ($fh, 'mappable') |
56
|
|
|
|
|
|
|
or croak "Handle not mappable"; |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
my $self = bless { key => $key, |
59
|
|
|
|
|
|
|
mmap => undef, |
60
|
|
|
|
|
|
|
}, $class; |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my $tell = tell($fh); |
63
|
|
|
|
|
|
|
if ($tell < 0) { |
64
|
|
|
|
|
|
|
# assume if tell() doesn't work then $fh is not mmappable, or in any |
65
|
|
|
|
|
|
|
# case don't know where the current position is to map |
66
|
|
|
|
|
|
|
croak "Cannot tell() file position: $!"; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# File::Map 0.38 does tainting itself |
70
|
|
|
|
|
|
|
# |
71
|
|
|
|
|
|
|
# # induce taint on the mmap -- seems to cause segvs though |
72
|
|
|
|
|
|
|
# read $fh, $self->{'mmap'}, 0; |
73
|
|
|
|
|
|
|
# use Devel::Peek; |
74
|
|
|
|
|
|
|
# Dump ($self->{'mmap'}); |
75
|
|
|
|
|
|
|
# |
76
|
|
|
|
|
|
|
# # crib: must taint before mapping, doesn't work afterwards |
77
|
|
|
|
|
|
|
# require Taint::Util; |
78
|
|
|
|
|
|
|
# Taint::Util::taint($self->{'mmap'}); |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
File::Map::map_handle ($self->{'mmap'}, $fh, '<', $tell); |
81
|
|
|
|
|
|
|
File::Map::advise ($self->{'mmap'}, 'sequential'); |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Scalar::Util::weaken ($cache{$key} = $self); |
84
|
|
|
|
|
|
|
$self; |
85
|
|
|
|
|
|
|
}); |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
# return a scalar ref to the mmapped string |
88
|
|
|
|
|
|
|
sub mmap_ref { |
89
|
29
|
|
|
29
|
0
|
55
|
my ($self) = @_; |
90
|
29
|
|
|
|
|
306
|
return \($self->{'mmap'}); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
sub DESTROY { |
93
|
29
|
|
|
29
|
|
24967
|
my ($self) = @_; |
94
|
29
|
|
|
|
|
559
|
delete $cache{$self->{'key'}}; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
use constant::defer _PAGESIZE => sub { |
98
|
4
|
|
|
|
|
1844
|
require POSIX; |
99
|
4
|
|
50
|
|
|
22428
|
my $pagesize = eval { POSIX::sysconf (POSIX::_SC_PAGESIZE()) } || -1; |
100
|
4
|
50
|
|
|
|
22
|
return ($pagesize > 0 ? $pagesize : 1024); |
101
|
4
|
|
|
4
|
|
1741
|
}; |
|
4
|
|
|
|
|
2841
|
|
|
4
|
|
|
|
|
32
|
|
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# return the total bytes used by mmaps here plus prospective further $space |
104
|
|
|
|
|
|
|
sub _total_space { |
105
|
10
|
|
|
10
|
|
34
|
my ($space) = @_; |
106
|
|
|
|
|
|
|
### total space of: $space, values(%cache) |
107
|
10
|
|
|
|
|
21
|
$space = _round_up_pagesize($space); |
108
|
10
|
|
|
|
|
30
|
foreach my $self (values %cache) { |
109
|
0
|
|
|
|
|
0
|
$space += _round_up_pagesize (length (${$self->mmap_ref})); |
|
0
|
|
|
|
|
0
|
|
110
|
|
|
|
|
|
|
} |
111
|
10
|
|
|
|
|
28
|
return $space; |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
sub _round_up_pagesize { |
114
|
12
|
|
|
12
|
|
277
|
my ($n) = @_; |
115
|
|
|
|
|
|
|
|
116
|
12
|
|
|
|
|
31
|
my $pagesize = _PAGESIZE(); |
117
|
12
|
|
|
|
|
179
|
return $pagesize * int (($n + $pagesize - 1) / $pagesize); |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# return true if $fh has an ":mmap" layer |
123
|
|
|
|
|
|
|
sub _have_mmap_layer { |
124
|
4
|
|
|
4
|
|
3378
|
my ($fh) = @_; |
125
|
4
|
|
|
|
|
6
|
my $ret; |
126
|
4
|
|
|
|
|
5
|
eval { |
127
|
4
|
|
|
|
|
17
|
require PerlIO; # new in perl 5.8 |
128
|
4
|
|
|
|
|
27
|
foreach my $layer (PerlIO::get_layers ($fh)) { |
129
|
11
|
100
|
|
|
|
23
|
if ($layer eq 'mmap') { $ret = 1; last; } |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2
|
|
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
}; |
132
|
4
|
|
|
|
|
47
|
return $ret; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# return true if mmapping $fh would be an excessive cumulative size |
136
|
|
|
|
|
|
|
sub _mmap_size_excessive { |
137
|
8
|
|
|
8
|
|
18
|
my ($fh) = @_; |
138
|
8
|
50
|
|
|
|
41
|
if (File::Locate::Iterator::FileMap->find($fh)) { |
139
|
|
|
|
|
|
|
# if already mapped then not excessive |
140
|
0
|
|
|
|
|
0
|
return 0; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# in 32-bits this is 4G*(1/4)*(1/5) which is 200Mb |
144
|
8
|
|
|
|
|
42
|
require Config; |
145
|
|
|
|
|
|
|
my $limit |
146
|
8
|
|
|
|
|
309
|
= (2 ** (8 * $Config::Config{'ptrsize'})) # eg. 2^32 bytes addr space |
147
|
|
|
|
|
|
|
* 0.25 # perhaps only 1/2 or 1/4 of it usable for data |
148
|
|
|
|
|
|
|
* 0.2; # then don't go past 1/5 of that usable space |
149
|
|
|
|
|
|
|
|
150
|
8
|
|
|
|
|
100
|
my $prosp = File::Locate::Iterator::FileMap::_total_space (-s $fh); |
151
|
|
|
|
|
|
|
### mmap size limit: $limit |
152
|
|
|
|
|
|
|
### file size: -s $fh |
153
|
|
|
|
|
|
|
### for new total: $prosp |
154
|
8
|
|
|
|
|
39
|
return ($prosp > $limit); |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
1; |
158
|
|
|
|
|
|
|
__END__ |