=begin pod :kind("Type") :subkind("role") :category("composite")

=TITLE role Buf

=SUBTITLE Mutable buffer for binary data

    role Buf[::T = uint8] does Blob[T] is repr('VMArray') is array_type(T){ ... }

A C<Buf> does the role of a mutable sequence of (usually unsigned) integers.

    my $b = Buf.new(1, 2, 3);
    $b[1] = 42;

However, it's a parameterized type, and you can instantiate with several
integer types:

=for code
my $b = Buf[int32].new(3, -3, 0xff32, -44);
say $b; # OUTPUT: «Buf[int32]:0x<03 -3 FF32 -2C>»

By default, C<Buf> uses 8-bit unsigned integers, that is, it is
equivalent to Buf[uint8]. Some other types of C<Buf>s which are used
often get their own class name.

X<|buf8>X<|buf16>X<|buf32>X<|buf64>
=begin table
buf8 | Buf[uint8]
buf16 | Buf[uint16]
buf32 | Buf[uint32]
buf64 | Buf[uint64]
=end table

You can use these in pretty much the same way you would with C<Buf>:

    my $buf = buf8.new(3, 6, 254);
    say $buf; # OUTPUT: «Buf[uint8]:0x<03 06 fe>␤»

Plus there are some object methods, like
L<C<encode>|/type/Str#method_encode> that might return a C<buf8> in some
cases where it is the best representation for a particular encoding.

=head1 Methods

=head2 method subbuf-rw

    method subbuf-rw($from = 0, $elems = self.elems - $from) is rw

A mutable version of C<subbuf> that returns a L<Proxy|/type/Proxy>
functioning as a writable reference to a part of a buffer. Its first
argument, C<$from> specifies the index in the buffer from which a
substitution should occur, and its last argument, C<$elems> specifies
how many elements are to be replaced.

For example, to replace one element at index 3 with two elements, C<100>
and C<101>:

    my Buf $b .= new(0..5);
    $b.subbuf-rw(3,1) = Buf.new(100, 101);
    say $b.raku;   # OUTPUT: «Buf.new(0,1,2,100,101,4,5)␤»


In the case the C<$elems> argument is not specified, the substitution
happens at the specified index C<$from> removing all trailing elements:

    my Buf $b .= new(0..5);
    $b.subbuf-rw(3) = Buf.new(200);
    say $b.raku;   # OUTPUT: «Buf.new(0,1,2,200)␤»

In the case the C<$from> argument is not specified, the substitution
happens from the very beginning of the buffer:

    my Buf $b .= new(0..5);
    $b.subbuf-rw = Buf.new(123, 123);
    say $b.raku;   # OUTPUT: «Buf.new(123, 123)␤»

=head2 routine subbuf-rw

Declared as

    multi sub subbuf-rw(Buf:D \b) is rw
    multi sub subbuf-rw(Buf:D \b, Int() $from) is rw
    multi sub subbuf-rw(Buf:D \b, $from, $elems) is rw

Returns a writable reference to a part of a buffer.
Invokes the C<subbuf-rw> method on the specified C<Buf>:

    my Buf $b .= new(1,2,3);
    subbuf-rw($b,2,1) = Buf.new(42);
    say $b.raku;   # OUTPUT: «Buf.new(1,2,42)␤»

=head2 method reallocate

    method reallocate(Buf:D: Int:D $elems)

Change the number of elements of the C<Buf>, returning the changed
C<Buf>. The size of C<Buf> will be adapted depending on the number of
C<$elems> specified: if it is smaller than the actual size of the C<Buf>
the resulting C<Buf> will be shrunk down, otherwise it will be enlarged
to fit the number of C<$elems>. In the case the C<Buf> is enlarged,
newly created items will be assigned a Virtual Machine specific null
value, therefore you should not rely upon their value since it could be
inconsistent across different virtual machines.


    my Buf $b .= new(^10);
    $b.reallocate(5);
    say $b.raku;  # OUTPUT: «Buf.new(0,1,2,3,4)␤»

    $b = Buf.new( 1..3 );
    $b.reallocate( 10 );
    $b.raku.say; # OUTPUT: «Buf.new(1,2,3,0,0,0,0,0,0,0)␤»

=head2 method list

Defined as:

    multi method list(Buf:D:)

Returns a C<List> of integers.

    say Buf.new(122,105,112,205).list; # OUTPUT: «(122 105 112 205)␤»

=head2 method push

    method push( $elems )

Adds elements at the end of the buffer

     my @φ =  1,1, * + * … ∞;
     my $bú = Buf.new( @φ[^5] );
     $bú.push( @φ[5] );
     say $bú.raku; # OUTPUT: «Buf.new(1,1,2,3,5,8)»

=head2 method pop

    method pop()

Extracts the last element of the buffer

=for code :preamble<my $bú>
say $bú.pop(); # OUTPUT: «8»
say $bú.raku; # OUTPUT: «Buf.new(1,1,2,3,5)»

=head2 method append

    method append( $elems )

Appends at the end of the buffer

=for code :preamble<my $bú; my @φ>
$bú.append( @φ[5..10] );
say $bú.raku; # OUTPUT: «Buf.new(1,1,2,3,5,8,13,21,34,55,89)»

=head2 method prepend

    method prepend( $elems )

Adds elements at the beginning of the buffer

=for code :preamble<my $bú>
$bú.prepend( 0 );
say $bú.raku; # OUTPUT: «Buf.new(0,1,1,2,3,5,8,13,21,34,55,89)»

=head2 method shift

    method shift()

Takes out the first element of the buffer

=for code :preamble<my $bú>
$bú.shift();
say $bú.raku; # OUTPUT: «Buf.new(1,1,2,3,5,8,13,21,34,55,89)»

=head2 method unshift

    method unshift()

Adds elements at the beginning of the buffer

=for code :preamble<my $bú>
$bú.unshift( 0 );
say $bú.raku; # OUTPUT: «Buf.new(0,1,1,2,3,5,8,13,21,34,55,89)»

=head2 method splice

    method splice( Buf:D: $start = 0, $elems?, *@replacement --> Buf)

Substitutes elements of the buffer by other elements

=for code :preamble<my $bú>
$bú.splice:  0, 3, <3 2 1>;
say $bú.raku; # OUTPUT: «Buf.new(3,2,1,2,3,5,8,13,21,34,55,89)»

=head1 Methods on buf8 only (6.d, 2018.12 and later)

These methods are available on the C<buf8> type only.  They allow low level
access to writing bytes to the underlying data and in different ways with
regards to type (integer or floating point (num)), size (8, 16, 32, 64 or 128
bits), signed or unsigned (for integer values) and endianness (native, little
and big endianness).  These methods always return C<Nil>.

Endianness must be indicated by using values of the L<Endian|/type/Endian>
enum as the B<third> parameter to these methods.  If no endianness is
specified, C<NativeEndian> will be assumed.  Other values are
C<LittleEndian> and C<BigEndian>.

The buffer will be automatically resized to support any bytes being written
if it is not large enough yet.

=head2 method write-uint8

Defined as:

    method write-uint8(buf8:D: uint $pos, uint8 $value, $endian = NativeEndian --> Nil)

Writes an unsigned 8-bit integer value at the given position.  The C<$endian>
parameter has no meaning, but is available for consistency.

=head2 method write-int8

Defined as:

    method write-int8(buf8:D: uint $pos, int8 $value, $endian = NativeEndian --> Nil)

Writes a signed 8-bit integer value at the given position.  The C<$endian>
parameter has no meaning, but is available for consistency.

=head2 method write-uint16

Defined as:

    method write-uint16(buf8:D: uint $pos, uint16 $value, $endian = NativeEndian --> Nil)

Writes an unsigned 16-bit integer value at the given position with the given
endianness.

=head2 method write-int16

Defined as:

    method write-int16(buf8:D: uint $pos, int16 $value, $endian = NativeEndian --> Nil)

Writes a signed 16-bit integer value at the given position with the given
endianness.

=head2 method write-uint32

Defined as:

    method write-uint32(buf8:D: uint $pos, uint32 $value, $endian = NativeEndian --> Nil)

Writes an unsigned 32-bit integer value at the given position with the given
endianness.

=head2 method write-int32

Defined as:

    method write-int32(buf8:D: uint $pos, int32 $value, $endian = NativeEndian --> Nil)

Writes a signed 32-bit integer value at the given position with the given
endianness.

=head2 method write-uint64

Defined as:

    method write-uint64(buf8:D: uint $pos, uint64 $value, $endian = NativeEndian --> Nil)

Writes an unsigned 64-bit integer value at the given position with the given
endianness.

=head2 method write-int64

Defined as:

    method write-int64(buf8:D: uint $pos, Int:D $value, $endian = NativeEndian --> Nil)

Writes a signed 64-bit integer value at the given position with the given
endianness.

=head2 method write-uint128

Defined as:

    method write-uint128(buf8:D: uint $pos, UInt:D $value, $endian = NativeEndian --> Nil)

Writes an unsigned 128-bit integer value at the given position with the given
endianness.

=head2 method write-int128

Defined as:

    method write-int128(buf8:D: uint $pos, Int:D $value, $endian = NativeEndian --> Nil)

Writes a signed 128-bit integer value at the given position with the given
endianness.

=head2 method write-num32

Defined as:

    method write-num32(buf8:D: uint $pos, num32 $value, $endian = NativeEndian --> Nil)

Writes a native C<num32> IEEE floating point value at the given position with
the given endianness.

=head2 method write-num64

Defined as:

    method write-num64(buf8:D: uint $pos, num64 $value, $endian = NativeEndian --> Nil)

Writes a native C<num64> IEEE floating point value at the given position with
the given endianness.

=head1 Methods on buf8 only (6.d, 2019.03 and later)

=head2 method write-ubits

Defined as:

    method write-ubits(buf8:D: uint $pos, uint $bits, UInt:D $value --> Nil)

Writes an unsigned integer value to the B<bits> from the given B<bit> offset
and given number of bits.  The endianness of the bits is assumed to be
C<BigEndian>.  Always returns Nil.

=head2 method write-bits

Defined as:

    method write-bits(buf8:D: uint $pos, uint $bits, Int:D $value --> Nil)

Writes a signed integer value for the B<bits> from the given B<bit> offset
and given number of bits.  The endianness of the bits is assumed to be
C<BigEndian>.  Always returns Nil.

=head1 Methods on buf8 only (6.d, 2019.10 and later)

These methods are available on the C<buf8> type only.  They allow low level
access to writing bytes to the underlying data and in different ways with
regards to type (integer or floating point (num)), size (8, 16, 32, 64 or 128
bits), signed or unsigned (for integer values) and endianness (native, little
and big endianness).

These methods can also be called on the C<buf8> type object, in which case
a new C<buf8> object will be returned.  Otherwise, the invocant will be
returned to allow for easier chaining of operations on the C<buf8> object.
The buffer will be automatically resized to support any bytes being written
if it is not large enough yet.

Endianness must be indicated by using values of the L<Endian|/type/Endian>
enum as the B<third> parameter to these methods.  If no endianness is
specified, C<NativeEndian> will be assumed.  Other values are
C<LittleEndian> and C<BigEndian>.

=head2 method write-uint8

Defined as:

    method write-uint8(buf8: uint $pos, uint8 $value, $endian = NativeEndian --> buf8:D)

Writes an unsigned 8-bit integer value at the given position.  The C<$endian>
parameter has no meaning, but is available for consistency.

=head2 method write-int8

Defined as:

    method write-int8(buf8: uint $pos, int8 $value, $endian = NativeEndian --> buf8:D)

Writes a signed 8-bit integer value at the given position.  The C<$endian>
parameter has no meaning, but is available for consistency.

=head2 method write-uint16

Defined as:

    method write-uint16(buf8: uint $pos, uint16 $value, $endian = NativeEndian --> buf8:D)

Writes an unsigned 16-bit integer value at the given position with the given
endianness.

=head2 method write-int16

Defined as:

    method write-int16(buf8: uint $pos, int16 $value, $endian = NativeEndian --> buf8:D)

Writes a signed 16-bit integer value at the given position with the given
endianness.

=head2 method write-uint32

Defined as:

    method write-uint32(buf8: uint $pos, uint32 $value, $endian = NativeEndian --> buf8:D)

Writes an unsigned 32-bit integer value at the given position with the given
endianness.

=head2 method write-int32

Defined as:

    method write-int32(buf8: uint $pos, int32 $value, $endian = NativeEndian --> buf8:D)

Writes a signed 32-bit integer value at the given position with the given
endianness.

=head2 method write-uint64

Defined as:

    method write-uint64(buf8: uint $pos, uint64 $value, $endian = NativeEndian --> buf8:D)

Writes an unsigned 64-bit integer value at the given position with the given
endianness.

=head2 method write-int64

Defined as:

    method write-int64(buf8: uint $pos, Int:D $value, $endian = NativeEndian --> buf8:D)

Writes a signed 64-bit integer value at the given position with the given
endianness.

=head2 method write-uint128

Defined as:

    method write-uint128(buf8: uint $pos, UInt:D $value, $endian = NativeEndian --> buf8:D)

Writes an unsigned 128-bit integer value at the given position with the given
endianness.

=head2 method write-int128

Defined as:

    method write-int128(buf8: uint $pos, Int:D $value, $endian = NativeEndian --> buf8:D)

Writes a signed 128-bit integer value at the given position with the given
endianness.

=head2 method write-num32

Defined as:

    method write-num32(buf8: uint $pos, num32 $value, $endian = NativeEndian --> buf8:D)

Writes a native C<num32> IEEE floating point value at the given position with
the given endianness.

=head2 method write-num64

Defined as:

    method write-num64(buf8: uint $pos, num64 $value, $endian = NativeEndian --> buf8:D)

Writes a native C<num64> IEEE floating point value at the given position with
the given endianness.

=head2 method write-ubits

Defined as:

    method write-ubits(buf8: uint $pos, uint $bits, UInt:D $value --> buf8:D)

Writes an unsigned integer value to the B<bits> from the given B<bit> offset
and given number of bits.  The endianness of the bits is assumed to be
C<BigEndian>.

=head2 method write-bits

Defined as:

    method write-bits(buf8: uint $pos, uint $bits, Int:D $value --> buf8:D)

Writes a signed integer value for the B<bits> from the given B<bit> offset
and given number of bits.  The endianness of the bits is assumed to be
C<BigEndian>.

=end pod

# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6
