#!/usr/bin/perl

=head1 NAME

CSSJ::Helpers - 入出力ユーティリティ

=head2 概要

ストリームへデータを入出力するためのユーティリティです。
これらの関数は、ノンブロッキングI/Oに対しても与えられた(要求される)データを全て出力(入力)します。

通常、プログラマがこのパッケージを直接使う必要はありません。

=head2 作者

$Date: 2006/03/30 08:35:00 $ MIYABE Tatsuhiko

=cut
package CSSJ::Helpers;

require Exporter;
@ISA	= qw(Exporter);

use strict;
use Symbol;

=head1 定数

=head2 BUFFER_SIZE

パケットの送信に使うバッファのサイズです。

=cut
sub BUFFER_SIZE { return 1024; }

=head1 write_int

C<write_int OUTHANDLE INTEGER>

32ビット数値をビッグインディアンで書き出します。

=head2 引数

=over

=item OUTHANDLE 出力先ハンドル

=item INTEGER 32ビット整数

=back

=head2 戻り値

B<成功なら書き込んだバイト数,失敗ならundef>

=cut
sub write_int (*$) {
  my $fp = shift;
  my $a = shift;
   $fp = Symbol::qualify_to_ref($fp, caller());
  
  my $data = pack('N', $a);
  return CSSJ::Helpers::write($fp, $data);
}

=head1 write_byte

C<write_byte OUTHANDLE BYTE>

8ビット数値を書き出します。

=head2 引数

=over

=item OUTHANDLE 出力先ハンドル

=item BYTE 8ビット整数

=back

=head2 戻り値

B<成功なら書き込んだバイト数,失敗ならundef>

=cut
sub write_byte (*$) {
  my $fp = shift;
  my $b = shift;
  $fp = Symbol::qualify_to_ref($fp, caller());
  
  my $data = chr($b);
  return CSSJ::Helpers::write($fp, $data);
}

=head1 write_bytes

C<write_bytes OUTHANDLE BYTES>

バイト数を16ビットビッグインディアンで書き出した後、バイト列を書き出します。

=head2 引数

=over

=item OUTHANDLE 出力先ハンドル

=item BYTES バイト列

=back

=head2 戻り値

B<成功なら書き込んだバイト数,失敗ならundef>

=cut
sub write_bytes (*$) {
  my $fp = shift;
  my $b = shift;
  $fp = Symbol::qualify_to_ref($fp, caller());
  
  my $data = pack('n', length($b));
  defined(CSSJ::Helpers::write($fp, $data)) or return undef;
  return CSSJ::Helpers::write($fp, $b);
}

=head1 write

C<write OUTHANDLE BYTES [ LENGTH] >

バイト列を書き出します。

=head2 引数

=over

=item OUTHANDLE 出力先ハンドル

=item BYTES バイト列

=item LENGTH 長さ

=back

=head2 戻り値

B<成功なら書き込んだバイト数,失敗ならundef>

=cut
sub write (*$;$) {
  my $fp = shift;
  my $data = shift;
  my $len = shift || length($data);
  $fp = Symbol::qualify_to_ref($fp, caller());
  
  my $off = 0;
  while ($len) {
    my $written;
    $written = syswrite($fp, $data, $len, $off);
    defined ($written) or return undef;
    $len -= $written;
    $off += $written;
  }
  return $off;
}

=head1 read_int

C<read_int INHANDLE INTEGER>

32ビットビッグインディアン数値を読み込みます。

=head2 引数

=over

=item INHANDLE 入力元ハンドル

=back

=head2 戻り値

B<成功なら32ビット整数,失敗ならundef>

=cut
sub read_int (*) {
  my $fp = shift;
  $fp = Symbol::qualify_to_ref($fp, caller());
  
  my $b = CSSJ::Helpers::read($fp, 4);
  defined($b) or return undef;
  $b = unpack('N', $b);
  if ($b >> 31!= 0) {
  	$b = -(($b ^ 0xFFFFFFFF) + 1);
  }
  return $b;
}

=head1 read_byte

C<read_byte INHANDLE>

8ビット数値を読み込みます。

=head2 引数

=over

=item INHANDLE 入力元ハンドル

=back

=head2 戻り値

B<成功なら8ビット整数,失敗ならundef>

=cut
sub read_byte (*) {
  my $fp = shift;
  $fp = Symbol::qualify_to_ref($fp, caller());
  
  my $b = CSSJ::Helpers::read($fp, 1);
  defined($b) or return undef;
  return ord($b);
}

=head1 read_bytes

C<read_bytes INHANDLE>

16ビットビッグインディアン数値を読み込み、そのバイト数だけバイト列を読み込みます。

=head2 引数

=over

=item INHANDLE 入力元ハンドル

=back

=head2 戻り値

B<成功ならバイト列,失敗ならundef>

=cut
sub read_bytes (*) {
  my $fp = shift;
  $fp = Symbol::qualify_to_ref($fp, caller());
  
  my $b = CSSJ::Helpers::read($fp, 2);
  defined($b) or return undef;
  my $len = unpack('n', $b);
  $b = CSSJ::Helpers::read($fp, $len);
  defined($b) or return undef;
  return $b;
}

=head1 read

C<read INHANDLE LENGTH>

バイト列を読み込みます。

=head2 引数

=over

=item INHANDLE 入力元ハンドル

=item LENGTH 要求されるバイト数

=back

=head2 戻り値

B<成功ならバイト列,失敗ならundef>

=cut
sub read (*$) {
  my $fp = shift;
  my $len = shift;
  $fp = Symbol::qualify_to_ref($fp, caller());
  
  my $result = '';
  for (;;) {
    if ($len <= 0) {
      return $result;
    }
    my $data;
    my $read = sysread($fp, $data, $len);
    defined($read) or return undef;
    $len -= $read;
    $result .= $data;
  }
}

