=encoding utf8

=head1 NAME

Module::Generic::Scalar::IO - Generic Module Scalar IO Class

=head1 SYNOPSIS

    my $s = Module::Generic::Scalar->new;
    my $io = $s->open || die( $s->error );
    # or
    my $io = Module::Generic::Scalar::IO->new( $scalar_object );
    # using PerlIO
    my $io = Module::Generic::Scalar::IO->new( $scalar_object, '+>:ut8' );
    my $io = Module::Generic::Scalar::IO->new( $scalar_object, '+>:binmode(ut-8)' );
    my $io = Module::Generic::Scalar::IO->new( $scalar_object, '+>:bytes' );

    $io->opened; # Return true if opened or false otherwise
    $io->fileno; # returns -1
    $io->flush;
    $io->print( <<EOT );
    Mignonne, allons voir si la rose
    Qui ce matin avoit desclose
    Sa robe de pourpre au Soleil,
    A point perdu cette vesprée
    Les plis de sa robe pourprée,
    Et son teint au vostre pareil.
    EOT
    $io->printf( "Author: %s\n", 'Pierre de Ronsard' );
    $io->getc; # return nothing, because we are at the end of file
    $io->eof; # return true if we are at the end of string
    $io->tell; # tells us our position in string
    $io->seek(0,0);
    my $l = $io->getline; # fetch the first line
    $io->seek( $io->length - 1, 0 );
    my $n = $io->write( ', Les Odes', 10 );
    $io->seek(0,0);
    @lines = $io->getlines;
    print $lines[-1], "\n";
    # Returns: Author: Pierre de Ronsard, Les Odes\n
    $io->close;

=head1 VERSION

    v0.2.1

=head1 DESCRIPTION

This class L<Module::Generic::Scalar::IO> implements object oriented IO methods on a scalar reference. It inherits from L<Module::Generic::File::IO> and you can use all the methods from L<IO::Handle>

Unlike the default Perl C<open()> behavior with a C<:scalar> layer, this class adds:

=over 4

=item *

Automatic support for opening modes like C<r>, C<w>, C<w+>, C<r+>, C<a>, etc.

=item *

Access to low-level flags via L<fcntl>, which are usually unavailable with plain scalar handles

=item *

Convenient methods to inspect open mode (e.g. L</is_readonly>, L</is_append>, L</is_writeonly>)

=item *

Safe serialisation support for use with Storable or Sereal

=item *

Automatic support for L<autoflush>, C<binmode>, and other standard I/O operations

=back

Thus, this makes it possible to use L<Module::Generic::Scalar> object as file handle to print and get data to and from it, and you can use it directly as well.

This normally can be achieved with L<PerlIO>, like this:

    my $ref = \"Hello";
    open( my $fh, '+<:scalar', $ref ) || die( $! );

However, this interface provides some additions such as automatically setting filehandle flags derived from the opening mode, allowing advanced features like C<fcntl> to work as expected.

    my $flags = $fh->fcntl( F_GETFL, 0 );
    $fh->fcntl( F_SETFL, O_RDWR );

This, in turn, allows the methods L</can_read> and L</can_write> and L</is_append>, L</is_create>, L</is_readonly>, L</is_readwrite>, L</is_writeonly> to work.

One shortcoming due to perl's own design, is if you call C<sysread( $fh, $buffer )> instead of C<$fh->sysread( $buffer )> or C<syswrite( $fh, $string )> instead of C<$fh->syswrite( $string )>. While the latter works of course, the former does not, so always make sure to call those methods in an object oriented way.

=head1 CONSTRUCTOR

=head2 new

    my $io = Module::Generic::Scalar::IO->new( \$scalar_ref, 'w' );
    my $io = Module::Generic::Scalar::IO->new({ scalar => \$scalar_ref, mode => 'r+' });

It takes a reference to a scalar, such as L<Module::Generic::Scalar> object or any other scalar reference, that will be treated as a memory buffer, and some optional mode and calls L</open>. It returns a blessed file handle.

If no mode is provided, it defaults to C<< +< >> to allow for read and write, but without clobbering.

Supported modes are:

=over 4

=item *  C<< < >> or C<r>

Read-only

=item *  C<< <+ >> or C<r+>

Read and write. This is the default.

=item *  C<< > >> or C<w>

Clobbering. This will empty the content of the scalar reference, before writing to it.

=item *  C<< +> >> or C<w+>

Cloberring, and read and write.

=item *  C<< >> >> or C<a>

Append mode. This will allow for appending data to the underlying scalar reference, but not read from it.

=item *  C<< +>> >> C<a+>

Append and read mode. This allows to append data and read from it.

=item * C<r>

Read-only

=item * C<w>

Write-only (truncate)

=item * C<a>

Append mode

=item * C<r+>, C<w+>, C<a+>

Read/write variants

=back

The mode will automatically be normalized to include C<:scalar> if not already present.

On failure to open, this method returns C<undef> and sets an error accessible via C<< $obj->error >>.

=head1 METHODS

=head2 autoflush

This is a no-ope; it does not do anything.

=head2 binmode

This is a no-ope; it does not do anything.

=head2 bit

Returns the bitwise value of the mode used to open the scalar reference io interface.

You can then use L<Fcntl> constants C<O_RDONLY>, C<O_RDWR>, C<O_CREAT>, C<O_WRONLY>, C<O_APPEND> to query in bitwise mode, such as:

    use Fcntl;
    if( $io->bit & O_RDWR )
    {
        say "Can write";
    }

=head2 can_read

Returns true if one can read from the scalar reference.

=head2 can_write

Returns true if one can write to the scalar reference.

=head2 clearerr

This is a no-ope; it does not do anything.

=head2 close

This merely untie the scalar. In the L<IO::Scalar>, the scalar reference passed are tied so they can be used in non-object oriented way also.

This method overrides the one from L<IO::Scalar> that would otherwise destroy our underlying L<Module::Generic::Scalar> object.

=head2 eof

Returns true if we are positioned at the end of the string, false otherwise.

=head2 fcntl

This is used to query or set the bitwise mode. For example:

    use Fcntl;
    my $bits = $io->fcntl( F_GETFL, $whatever );
    # Set bits. Here same as +>
    $io->fcntl( F_SETFL, ( O_CREAT | O_RDWR ) );

=head2 fileno

As perl documentation for L<perlfunc/open>, this returns C<-1>

=head2 flush

This causes perl to flush any buffered data at the perlio api level. Any unread data in the buffer will be discarded, and any unwritten data will be written to the underlying file descriptor.

It returns "0 but true" on success, and upon error sets an L<error/Module::Generic/error> and returns C<undef>.

=head2 getc

Return the next character from our last position, or C<undef> if none remains.

=head2 getline

Return the next line, or undef on end of string.

=head2 getlines

Get all the lines from the position we are in the string.

So using our example in the L</SYNOPSIS> above :

    $io->seek(0,0); # position ourself at the start of the string
    my $c = $io->getc; # get the first character, which is 'M'
    my @lines = $io->getlines;

This will fetch all 6 lines, except the first one will only contain:

    ignonne, allons voir si la rose

i.e. without the leading "M", since L</getc> positioned us after.

So, be careful about your position in the string.

This can only be called in list context, or this will return an L<error|Module::Generic/error>

=for Pod::Coverage getpos

=head2 is_append

Returns true if the bitwise mode has the C<O_APPEND> bit enabled, false otherwise.

=head2 is_create

Returns true if the bitwise mode has the C<O_CREAT> bit enabled, false otherwise.

=head2 is_readonly

Returns true if the bitwise mode has the C<O_RDONLY> bit enabled, false otherwise.

=head2 is_readwrite

Returns true if the bitwise mode has the C<O_RDWR> bit enabled, false otherwise.

=head2 is_writeonly

Returns true if the bitwise mode has the C<O_WRONLY> bit enabled, false otherwise.

=head2 length

Returns the size of the underlining scalar reference in bytes.

=head2 line

Provided with a callback as a subroutine reference or anonymous subroutine, and this will call the callback passing it each line of the scalar.

If the callback returns C<undef>, this will terminate the browsing of each line, unless the option I<auto_next> is set. See below.

It takes some optional arguments as follow:

=over 4

=item I<chomp> boolean

If true, each line will be L<perlfunc/chomp>'ed before being passed to the callback.

=item I<auto_next> boolean

If true, this will ignore the return value from the callback and will move on to the next line.

=back

=head2 object

Returns the underlying L<Module::Generic::Scalar> object or any other scalar reference that was provided during object instantiation.

=head2 open

This takes an L<Module::Generic::Scalar> object or any other scalar reference and a mode. See L</new> for the list of supported modes.

This will reeturn an L<error|Module::Generic/error> if the scalar reference provided is not appropriate or if the mode provided is unsupported.

It returns the current object this was called with. It will return an error if it is called as a class function and not using an object.

=head2 opened

Returns true if the filehanle is opened or false otherwise.

=head2 print

This prints at the last position in the string the list of data provided, just like L<perlfunc/print>

If you want to make sure you are at the end, do:

    $io->seek(0,2);
    # or better yet:
    $io->seek(0,SEEK_END);

=head2 printf

Same as L<perlfunc/printf>

=head2 read

    my $buff;
    $io->read( $buff, 1024 );
    # or
    $io->read( $buff, 1024, $offset );

Same as L<perlfunc/read>

Takes a string as a buffer, a length and an optional offset in the buffer and will attempt to read from our scalar the requested length and place the result in the buffer.

It returns the number of data read at the last position, or C<undef> if there was an error.

=head2 say

Same as L<perlfunc/say>

=head2 seek

    $io->seek( POSITION, WHENCE );

This takes 2 arguments: an integer representing a position in the string and another integer representing the action to take.

Quoting from L<perlfunc/seek>: "The values for WHENCE are 0 to set the new position *in bytes* to POSITION; 1 to set it to the current position plus POSITION; and 2 to set it to EOF plus POSITION, typically negative."

You can also use L<Fcntl>, such as:

    $io->seek( $some_pos, SEEK_SET );

=for Pod::Coverage setpos

=head2 size

Alias for L</length>

=for Pod::Coverage sref

=head2 stat

This is a no-op and always returns C<undef> or an empty list in list context.

=head2 sysread

Same as L<perlfunc/sysread>

=head2 sysseek

Same as L<perlfunc/sysseek>

=head2 syswrite

Same as L<perlfunc/syswrite>

=head2 tell

Same as L<perlfunc/tell>

Returns the current position in the string.

=head2 truncate

    $io->truncate( $length );
    $io->truncate( $io->tell );

Same as L<perlfunc/truncate>

This truncates the string to the specified length C<$length>. It returns true if successful, and false otherwise, such as when at the end of the string.

The position in the string is left unchanged. You may want to call seek before writing to the string.

This is an improvement from L<IO::Handle/truncate>, which would otherwise fail on a scalar reference and return C<Bad file descriptor>

=for Pod::Coverage ungetc

=head2 write

    $io->write( $data );
    $io->write( $data, 1024 );
    $io->write( $data, 1024, $offset );

Same as L<IO::Handle/write>

This takes some data C<$data>, and optionally some length of those data to take and optionally at a given C<$offset> in C<$data>, and will L</print> those data at the last position in string.

This is an improvement from L<IO::Handle/write>, which does a simple C<print> and thus returns only true or false.

It returns the number of bytes printed, or C<undef> if there was an error, in which cases the error message can be retrieved with L<error|Module::Generic/error>

=head1 SERIALISATION

=for Pod::Coverage FREEZE

=for Pod::Coverage STORABLE_freeze

=for Pod::Coverage STORABLE_thaw

=for Pod::Coverage THAW

=for Pod::Coverage TO_JSON

Serialisation by L<CBOR|CBOR::XS>, L<Sereal> and L<Storable::Improved> (or the legacy L<Storable>) is supported by this package. To that effect, the following subroutines are implemented: C<FREEZE>, C<THAW>, C<STORABLE_freeze> and C<STORABLE_thaw>

=head1 THREAD SAFETY

This module is thread-safe as long as each thread uses its own distinct scalar reference.

If you share the same scalar reference across threads (e.g., using C<threads::shared>), then you must coordinate access using external locking mechanisms (e.g., C<lock()>).

No internal locking is performed by this module, by design.

=head1 SEE ALSO

L<Module::Generic::Scalar>, L<PerlIO::scalar>, L<Module::Generic::File::IO>

=head1 AUTHOR

Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>

=head1 COPYRIGHT & LICENSE

Copyright (c) 2021-2024 DEGUEST Pte. Ltd.

You can use, copy, modify and redistribute this package and associated
files under the same terms as Perl itself.

=cut

