has
[
qw/cleanup path/
];
has
handle
=>
sub
{
my
$self
=
shift
;
my
$handle
= IO::File->new;
my
$file
=
$self
->path;
if
(
$file
&& -f
$file
) {
$handle
->
open
(
"< $file"
)
or croak
qq/Can't open file "$file": $!/
;
return
$handle
;
}
my
$base
= File::Spec->catfile(
$self
->tmpdir,
'mojo.tmp'
);
my
$name
=
$file
||
$base
;
my
$fh
;
until
(
sysopen
$fh
,
$name
, O_CREAT | O_EXCL | O_RDWR) {
croak
qq/Can't open file "$name": $!/
if
$file
|| $! != $!{EEXIST};
$name
=
"$base."
. md5_sum(
time
. $$ .
rand
999999999);
}
$file
=
$name
;
$self
->path(
$file
);
$self
->cleanup(1);
$handle
->fdopen(
fileno
(
$fh
),
"+>"
) or croak
qq/Can't open file "$name": $!/
;
return
$handle
;
};
has
tmpdir
=>
sub
{
$ENV
{MOJO_TMPDIR} || File::Spec->tmpdir };
sub
DESTROY {
my
$self
=
shift
;
my
$path
=
$self
->path;
if
(
$self
->cleanup && -f
$path
) {
close
$self
->handle;
unlink
$path
;
}
}
sub
add_chunk {
my
(
$self
,
$chunk
) =
@_
;
$self
->handle->
sysseek
(0, SEEK_END);
$chunk
=
''
unless
defined
$chunk
;
utf8::encode
$chunk
if
utf8::is_utf8
$chunk
;
$self
->handle->
syswrite
(
$chunk
,
length
$chunk
);
return
$self
;
}
sub
contains {
my
(
$self
,
$pattern
) =
@_
;
$self
->handle->
sysseek
(
$self
->start_range, SEEK_SET);
my
$end
=
defined
$self
->end_range ?
$self
->end_range :
$self
->size;
my
$window_size
=
length
(
$pattern
) * 2;
$window_size
=
$end
-
$self
->start_range
if
$window_size
>
$end
-
$self
->start_range;
my
$read
=
$self
->handle->
sysread
(
my
$window
,
$window_size
);
my
$offset
=
$read
;
my
$pattern_size
=
length
(
$pattern
);
my
$range
=
$self
->end_range;
while
(
$offset
<=
$end
) {
if
(
defined
$range
) {
$pattern_size
=
$end
+ 1 -
$offset
;
return
-1
if
$pattern_size
<= 0;
}
$read
=
$self
->handle->
sysread
(
my
$buffer
,
$pattern_size
);
$offset
+=
$read
;
$window
.=
$buffer
;
my
$pos
=
index
$window
,
$pattern
;
return
$pos
if
$pos
>= 0;
return
-1
if
$read
== 0;
substr
$window
, 0,
$read
,
''
;
}
return
-1;
}
sub
get_chunk {
my
(
$self
,
$start
) =
@_
;
$start
+=
$self
->start_range;
$self
->handle->
sysseek
(
$start
, SEEK_SET);
my
$end
=
$self
->end_range;
my
$buffer
;
my
$size
=
$ENV
{MOJO_CHUNK_SIZE} || 262144;
if
(
defined
$end
) {
my
$chunk
=
$end
+ 1 -
$start
;
return
''
if
$chunk
<= 0;
$chunk
=
$size
if
$chunk
>
$size
;
$self
->handle->
sysread
(
$buffer
,
$chunk
);
}
else
{
$self
->handle->
sysread
(
$buffer
,
$size
) }
return
$buffer
;
}
sub
move_to {
my
(
$self
,
$path
) =
@_
;
close
$self
->handle;
delete
$self
->{handle};
my
$src
=
$self
->path;
File::Copy::move(
$src
,
$path
)
or croak
qq/Can't move file "$src" to "$path": $!/
;
$self
->path(
$path
);
$self
->cleanup(0);
return
$self
;
}
sub
size {
my
$self
=
shift
;
my
$file
=
$self
->path;
return
-s
$file
if
$file
;
return
0;
}
sub
slurp {
my
$self
=
shift
;
$self
->handle->
sysseek
(0, SEEK_SET);
my
$content
=
''
;
while
(
$self
->handle->
sysread
(
my
$buffer
, 131072)) {
$content
.=
$buffer
}
return
$content
;
}
1;