/usr/share/perl5/File/RandomAccess.pm is in libimage-exiftool-perl 8.60-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | #------------------------------------------------------------------------------
# File: RandomAccess.pm
#
# Description: Buffer to support random access reading of sequential file
#
# Revisions: 02/11/2004 - P. Harvey Created
# 02/20/2004 - P. Harvey Added flag to disable SeekTest in new()
# 11/18/2004 - P. Harvey Fixed bug with seek relative to end of file
# 01/02/2005 - P. Harvey Added DEBUG code
# 01/09/2006 - P. Harvey Fixed bug in ReadLine() when using
# multi-character EOL sequences
# 02/20/2006 - P. Harvey Fixed bug where seek past end of file could
# generate "substr outside string" warning
# 06/10/2006 - P. Harvey Decreased $CHUNK_SIZE from 64k to 8k
# 11/23/2006 - P. Harvey Limit reads to < 0x80000000 bytes
# 11/26/2008 - P. Harvey Fixed bug in ReadLine when reading from a
# scalar with a multi-character newline
# 01/24/2009 - PH Protect against reading too much at once
#
# Notes: Calls the normal file i/o routines unless SeekTest() fails, in
# which case the file is buffered in memory to allow random access.
# SeekTest() is called automatically when the object is created
# unless specified.
#
# May also be used for string i/o (just pass a scalar reference)
#
# Legal: Copyright (c) 2003-2010 Phil Harvey (phil at owl.phy.queensu.ca)
# This library is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#------------------------------------------------------------------------------
package File::RandomAccess;
use strict;
require 5.002;
require Exporter;
use vars qw($VERSION @ISA @EXPORT_OK);
$VERSION = '1.10';
@ISA = qw(Exporter);
sub Read($$$);
# constants
my $CHUNK_SIZE = 8192; # size of chunks to read from file (must be power of 2)
my $SLURP_CHUNKS = 16; # read this many chunks at a time when slurping
#------------------------------------------------------------------------------
# Create new RandomAccess object
# Inputs: 0) reference to RandomAccess object or RandomAccess class name
# 1) file reference or scalar reference
# 2) flag set if file is already random access (disables automatic SeekTest)
sub new($$;$)
{
my ($that, $filePt, $isRandom) = @_;
my $class = ref($that) || $that;
my $self;
if (ref $filePt eq 'SCALAR') {
# string i/o
$self = {
BUFF_PT => $filePt,
POS => 0,
LEN => length($$filePt),
TESTED => -1,
};
bless $self, $class;
} else {
# file i/o
my $buff = '';
$self = {
FILE_PT => $filePt, # file pointer
BUFF_PT => \$buff, # reference to file data
POS => 0, # current position in file
LEN => 0, # data length
TESTED => 0, # 0=untested, 1=passed, -1=failed (requires buffering)
};
bless $self, $class;
$self->SeekTest() unless $isRandom;
}
return $self;
}
#------------------------------------------------------------------------------
# Enable DEBUG code
# Inputs: 0) reference to RandomAccess object
sub Debug($)
{
my $self = shift;
$self->{DEBUG} = { };
}
#------------------------------------------------------------------------------
# Perform seek test and turn on buffering if necessary
# Inputs: 0) reference to RandomAccess object
# Returns: 1 if seek test passed (ie. no buffering required)
# Notes: Must be done before any other i/o
sub SeekTest($)
{
my $self = shift;
unless ($self->{TESTED}) {
my $fp = $self->{FILE_PT};
if (seek($fp, 1, 1) and seek($fp, -1, 1)) {
$self->{TESTED} = 1; # test passed
} else {
$self->{TESTED} = -1; # test failed (requires buffering)
}
}
return $self->{TESTED} == 1 ? 1 : 0;
}
#------------------------------------------------------------------------------
# Get current position in file
# Inputs: 0) reference to RandomAccess object
# Returns: current position in file
sub Tell($)
{
my $self = shift;
my $rtnVal;
if ($self->{TESTED} < 0) {
$rtnVal = $self->{POS};
} else {
$rtnVal = tell($self->{FILE_PT});
}
return $rtnVal;
}
#------------------------------------------------------------------------------
# Seek to position in file
# Inputs: 0) reference to RandomAccess object
# 1) position, 2) whence (0 or undef=from start, 1=from cur pos, 2=from end)
# Returns: 1 on success
# Notes: When buffered, this doesn't quite behave like seek() since it will return
# success even if you seek outside the limits of the file. However if you
# do this, you will get an error on your next Read().
sub Seek($$;$)
{
my ($self, $num, $whence) = @_;
$whence = 0 unless defined $whence;
my $rtnVal;
if ($self->{TESTED} < 0) {
my $newPos;
if ($whence == 0) {
$newPos = $num; # from start of file
} elsif ($whence == 1) {
$newPos = $num + $self->{POS}; # relative to current position
} else {
$self->Slurp(); # read whole file into buffer
$newPos = $num + $self->{LEN}; # relative to end of file
}
if ($newPos >= 0) {
$self->{POS} = $newPos;
$rtnVal = 1;
}
} else {
$rtnVal = seek($self->{FILE_PT}, $num, $whence);
}
return $rtnVal;
}
#------------------------------------------------------------------------------
# Read from the file
# Inputs: 0) reference to RandomAccess object, 1) buffer, 2) bytes to read
# Returns: Number of bytes read
sub Read($$$)
{
my $self = shift;
my $len = $_[1];
my $rtnVal;
# protect against reading too much at once
# (also from dying with a "Negative length" error)
if ($len & 0xf8000000) {
return 0 if $len < 0;
# read in smaller blocks because Windows attempts to pre-allocate
# memory for the full size, which can lead to an out-of-memory error
my $maxLen = 0x4000000; # (MUST be less than bitmask in "if" above)
my $num = Read($self, $_[0], $maxLen);
return $num if $num < $maxLen;
for (;;) {
$len -= $maxLen;
last if $len <= 0;
my $l = $len < $maxLen ? $len : $maxLen;
my $buff;
my $n = Read($self, $buff, $l);
last unless $n;
$_[0] .= $buff;
$num += $n;
last if $n < $l;
}
return $num;
}
# read through our buffer if necessary
if ($self->{TESTED} < 0) {
my $buff;
my $newPos = $self->{POS} + $len;
# number of bytes to read from file
my $num = $newPos - $self->{LEN};
if ($num > 0 and $self->{FILE_PT}) {
# read data from file in multiples of $CHUNK_SIZE
$num = (($num - 1) | ($CHUNK_SIZE - 1)) + 1;
$num = read($self->{FILE_PT}, $buff, $num);
if ($num) {
${$self->{BUFF_PT}} .= $buff;
$self->{LEN} += $num;
}
}
# number of bytes left in data buffer
$num = $self->{LEN} - $self->{POS};
if ($len <= $num) {
$rtnVal = $len;
} elsif ($num <= 0) {
$_[0] = '';
return 0;
} else {
$rtnVal = $num;
}
# return data from our buffer
$_[0] = substr(${$self->{BUFF_PT}}, $self->{POS}, $rtnVal);
$self->{POS} += $rtnVal;
} else {
# read directly from file
$_[0] = '' unless defined $_[0];
$rtnVal = read($self->{FILE_PT}, $_[0], $len) || 0;
}
if ($self->{DEBUG}) {
my $pos = $self->Tell() - $rtnVal;
unless ($self->{DEBUG}->{$pos} and $self->{DEBUG}->{$pos} > $rtnVal) {
$self->{DEBUG}->{$pos} = $rtnVal;
}
}
return $rtnVal;
}
#------------------------------------------------------------------------------
# Read a line from file (end of line is $/)
# Inputs: 0) reference to RandomAccess object, 1) buffer
# Returns: Number of bytes read
sub ReadLine($$)
{
my $self = shift;
my $rtnVal;
my $fp = $self->{FILE_PT};
if ($self->{TESTED} < 0) {
my ($num, $buff);
my $pos = $self->{POS};
if ($fp) {
# make sure we have some data after the current position
while ($self->{LEN} <= $pos) {
$num = read($fp, $buff, $CHUNK_SIZE);
return 0 unless $num;
${$self->{BUFF_PT}} .= $buff;
$self->{LEN} += $num;
}
# scan and read until we find the EOL (or hit EOF)
for (;;) {
$pos = index(${$self->{BUFF_PT}}, $/, $pos);
if ($pos >= 0) {
$pos += length($/);
last;
}
$pos = $self->{LEN}; # have scanned to end of buffer
$num = read($fp, $buff, $CHUNK_SIZE) or last;
${$self->{BUFF_PT}} .= $buff;
$self->{LEN} += $num;
}
} else {
# string i/o
$pos = index(${$self->{BUFF_PT}}, $/, $pos);
if ($pos < 0) {
$pos = $self->{LEN};
$self->{POS} = $pos if $self->{POS} > $pos;
} else {
$pos += length($/);
}
}
# read the line from our buffer
$rtnVal = $pos - $self->{POS};
$_[0] = substr(${$self->{BUFF_PT}}, $self->{POS}, $rtnVal);
$self->{POS} = $pos;
} else {
$_[0] = <$fp>;
if (defined $_[0]) {
$rtnVal = length($_[0]);
} else {
$rtnVal = 0;
}
}
if ($self->{DEBUG}) {
my $pos = $self->Tell() - $rtnVal;
unless ($self->{DEBUG}->{$pos} and $self->{DEBUG}->{$pos} > $rtnVal) {
$self->{DEBUG}->{$pos} = $rtnVal;
}
}
return $rtnVal;
}
#------------------------------------------------------------------------------
# Read whole file into buffer (without changing read pointer)
# Inputs: 0) reference to RandomAccess object
sub Slurp($)
{
my $self = shift;
my $fp = $self->{FILE_PT} || return;
# read whole file into buffer (in large chunks)
my ($buff, $num);
while (($num = read($fp, $buff, $CHUNK_SIZE * $SLURP_CHUNKS)) != 0) {
${$self->{BUFF_PT}} .= $buff;
$self->{LEN} += $num;
}
}
#------------------------------------------------------------------------------
# set binary mode
# Inputs: 0) reference to RandomAccess object
sub BinMode($)
{
my $self = shift;
binmode($self->{FILE_PT}) if $self->{FILE_PT};
}
#------------------------------------------------------------------------------
# close the file and free the buffer
# Inputs: 0) reference to RandomAccess object
sub Close($)
{
my $self = shift;
if ($self->{DEBUG}) {
local $_;
if ($self->Seek(0,2)) {
$self->{DEBUG}->{$self->Tell()} = 0; # set EOF marker
my $last;
my $tot = 0;
my $bad = 0;
foreach (sort { $a <=> $b } keys %{$self->{DEBUG}}) {
my $pos = $_;
my $len = $self->{DEBUG}->{$_};
if (defined $last and $last < $pos) {
my $bytes = $pos - $last;
$tot += $bytes;
$self->Seek($last);
my $buff;
$self->Read($buff, $bytes);
my $warn = '';
if ($buff =~ /[^\0]/) {
$bad += ($pos - $last);
$warn = ' - NON-ZERO!';
}
printf "0x%.8x - 0x%.8x (%d bytes)$warn\n", $last, $pos, $bytes;
}
my $cur = $pos + $len;
$last = $cur unless defined $last and $last > $cur;
}
print "$tot bytes missed";
$bad and print ", $bad non-zero!";
print "\n";
} else {
warn "File::RandomAccess DEBUG not working (file already closed?)\n";
}
delete $self->{DEBUG};
}
# close the file
if ($self->{FILE_PT}) {
close($self->{FILE_PT});
delete $self->{FILE_PT};
}
# reset the buffer
my $emptyBuff = '';
$self->{BUFF_PT} = \$emptyBuff;
$self->{LEN} = 0;
$self->{POS} = 0;
}
#------------------------------------------------------------------------------
1; # end
|