<commit_before>unit class Net::SSH::Channel;
use v6.c;
use NativeCall;
use Net::SSH::LibSSH;
use Net::SSH::Exceptions;

has Pointer $!channel;
has Pointer $.sess;
has Str $.host;

subset BufferSize of Int where 32 < * < 1048576 ;

submethod BUILD( :$!sess, :$!host ){

    $!channel = ssh_channel_new($!sess);

    if $!channel.Bool == False {
        self.close;
        fail X::SSH::Channel.new( :$!host );
    }

    my int $res = ssh_channel_open_session($!channel);

    if $res != SSH_OK {
        self.close;
        fail X::SSH::Channel.new( :$!host );
    }

    return $!channel;
}

method exec(Str $cmd) {

    my int $nbytes;
    my int $res     = ssh_channel_request_exec($!channel, $cmd);

    if $res != SSH_OK {
        note X::SSH::Exec.new( :$.host, :$cmd, :error(self!get_error) );
        self.close;
        return Nil;
    }

    return True;
}

method !read-sync( Buf :$STDOUT! is rw, Buf :$STDERR! is rw, Bool :$stderr = False, BufferSize :$buffer-size = 255 ) {

    my $buffer      = CArray[int8].new;
    $buffer[$buffer-size]    = 0;

    my $rbytes = ssh_channel_read($!channel, $buffer, $buffer.elems, $stderr.Int);

    if $rbytes < 0 {
        self.close;
        return -1; # That actualy breaks read() method and returns Nil
    } elsif $rbytes > 0 {

        if $rbytes > 0 && $rbytes != $buffer.elems {
            $buffer = CArray[int8].new( $buffer.list[0..$rbytes-1] );
            $rbytes = 0; # LAST read - do not try more
        }

        $STDOUT.append: $buffer.list if !$stderr;
        $STDERR.append: $buffer.list if $stderr;
    }

    return $rbytes;
}

# Max 1MB buffer
method read(BufferSize :$buffer-size = 255 ) {
    my Int $nbytes;

    my Buf $STDOUT .= new;
    my Buf $STDERR .= new;

    $nbytes = self!read-sync( :$STDOUT, :$STDERR, :$buffer-size );

    if $nbytes < 0 {
        return Nil;
    }

    while $nbytes > 0 {
        $nbytes = self!read-sync( :$STDOUT, :$STDERR, :$buffer-size );
    }<commit_msg>improve default read buffer size, now its type is checked if is power of 2<commit_after>unit class Net::SSH::Channel;
use v6.c;
use NativeCall;
use Net::SSH::LibSSH;
use Net::SSH::Exceptions;

has Pointer $!channel;
has Pointer $.sess;
has Str $.host;

subset BufferSize of Int where -> $i { $i > 0 && ($i +& ($i-1)) == 0 }  ; # Buffers can be only power of 2

submethod BUILD( :$!sess, :$!host ){

    $!channel = ssh_channel_new($!sess);

    if $!channel.Bool == False {
        self.close;
        fail X::SSH::Channel.new( :$!host );
    }

    my int $res = ssh_channel_open_session($!channel);

    if $res != SSH_OK {
        self.close;
        fail X::SSH::Channel.new( :$!host );
    }

    return $!channel;
}

method exec(Str $cmd) {

    my int $nbytes;
    my int $res     = ssh_channel_request_exec($!channel, $cmd);

    if $res != SSH_OK {
        note X::SSH::Exec.new( :$.host, :$cmd, :error(self!get_error) );
        self.close;
        return Nil;
    }

    return True;
}

method !read-sync( Buf :$STDOUT! is rw, Buf :$STDERR! is rw, Bool :$stderr = False, BufferSize :$buffer-size = 256 ) {

    my $buffer      = CArray[int8].new;
    $buffer[$buffer-size-1]    = 0;

    my $rbytes = ssh_channel_read($!channel, $buffer, $buffer.elems, $stderr.Int);

    if $rbytes < 0 {
        self.close;
        return -1; # That actualy breaks read() method and returns Nil
    } elsif $rbytes > 0 {

        if $rbytes > 0 && $rbytes != $buffer.elems {
            $buffer = CArray[int8].new( $buffer.list[0..$rbytes-1] );
            $rbytes = 0; # LAST read - do not try more
        }

        $STDOUT.append: $buffer.list if !$stderr;
        $STDERR.append: $buffer.list if $stderr;
    }

    return $rbytes;
}

# Max 1MB buffer
method read(BufferSize :$buffer-size = 256 ) {
    my Int $nbytes;

    my Buf $STDOUT .= new;
    my Buf $STDERR .= new;

    $nbytes = self!read-sync( :$STDOUT, :$STDERR, :$buffer-size );

    if $nbytes < 0 {
        return Nil;
    }

    while $nbytes > 0 {
        $nbytes = self!read-sync( :$STDOUT, :$STDERR, :$buffer-size );
    }
{'fraction_non_alphanumeric': 0.16141732283464566, 'fraction_numerical': 0.013533464566929134, 'mean_word_length': 2.4536958368734068, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1515306', 'n_tokens_mistral': 1597, 'n_tokens_neox': 1472, 'n_words': 391}