package Plucene::Store::InputStream;

=head1 NAME 

Plucene::Store::InputStream - a random-access input stream

=head1 SYNOPSIS

	# isa IO::File

=head1 DESCRIPTION

A random-access input stream.Used for all Plucene index input operations.

=head1 METHODS

=cut

use strict;
use warnings;

use Encode qw(_utf8_on);    # Magic
use File::Spec::Functions;
use Cwd;
use IO::File;
use Class::HasA ([qw/ read seek tell getc print eof close /] => "fh");

=head2 new

	my $inputstream = Plucene::Store::InputStream->new($file);

Create a new input stream.

=cut

sub new {
	my ($self, $filename, $mode) = @_;
	$self = ref $self || $self;
	$mode ||= "r";
	my $abs = canonpath($filename);
	my $fh = IO::File->new($abs, $mode) or die "$self, $mode: $filename $!";
	bless { fh => $fh, path => $abs, mode => $mode }, $self;
}

=head2 fh

The filehandle

=cut

sub fh { $_[0]->{fh} }

=head2 clone

This will return a clone of this stream.

=cut

sub clone {
	my $orig = shift;
	my $clone = $orig->new($orig->{path}, $orig->{mode});
	$clone->seek($orig->tell, 0);
	return $clone;
}

=head2 read_byte

This will read and return a single byte.

=cut

sub read_byte {
	my $self = shift;
	confess("Unexpectedly hit EOF") if $self->eof;
	ord $self->getc;
}

=head2 read_int

This will read four bytes and return an integer.

=cut

sub read_int {
	my $self = shift;
	my $buf  = "\0" x 4;
	$self->read($buf, 4);
	return unpack("N", $buf);
}

=head2 read_vint

This will read an integer stored in a variable-length format.

=cut

sub read_vint {
	my $self = shift;
	my $b    = $self->read_byte();
	my $i    = $b & 0x7F;
	for (my $s = 7 ; ($b & 0x80) != 0 ; $s += 7) {
		$b = $self->read_byte();
		$i |= ($b & 0x7F) << $s;
	}
	return $i;
}

=head2 read_vlong

This will read a long and stored in variable-length format

=cut

*read_vlong = *read_vint;    # Perl is type-agnostic. ;)

=head2 read_string

This will read a string.

=cut

sub read_string {
	my $self   = shift;
	my $length = $self->read_vint();
	my $utf8;
	$self->read($utf8, $length);
	_utf8_on($utf8);
	return $utf8;
}

=head2 read_long

This will read eight bytes and return a long.

=cut

sub read_long {
	my $self  = shift;
	my $int_a = $self->read_int;
	my $int_b = $self->read_int;    # Order is important!
	return (($int_a << 32) | ($int_b & 0xFFFFFFFF));
}

1;
