Buf — 이진 데이터를 위한 변경 가능한 버퍼

Buf — 이진 데이터를 위한 변경 가능한 버퍼

원시 바이트를 담는 버퍼를 만들고, 그 값을 바꿔가며 다루고 싶을 때가 있어요. Buf는 그런 "변경 가능한(mutable) 정수 시퀀스" 역할을 하는 타입입니다.

출처: Raku 공식 문서 — Buf

본문

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

Buf는 (보통 부호 없는) 정수의 변경 가능한 시퀀스 역할을 담당해요.

my $bú = Buf.new(1, 2, 3);
$bú[1] = 42;
say $bú.raku; # OUTPUT: «Buf.new(1,42,3)␤»

그런데 이건 매개변수화된 타입이라서, 여러 정수 타입으로 인스턴스를 만들 수 있어요.

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

기본적으로 Buf는 8비트 부호 없는 정수를 쓰는데, 즉 Buf[uint8]과 동등해요. 자주 쓰이는 몇 가지 Buf 타입은 따로 클래스 이름이 정해져 있습니다.

buf8 Buf[uint8]
buf16 Buf[uint16]
buf32 Buf[uint32]
buf64 Buf[uint64]

Buf를 쓰는 것처럼 똑같이 사용할 수 있어요.

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

다른 객체에 있는 일부 메서드, 예를 들어 encode는 특정 인코딩에 가장 적합한 표현이 buf8일 때 buf8을 반환할 수 있어요.

메서드

method subbuf-rw

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

버퍼의 일부분을 가리키는 쓰기 가능한 참조 역할을 하는 Proxy를 반환하는 subbuf의 변경 가능한 버전이에요. 첫 번째 인자 $from은 치환이 일어나야 할 버퍼 내 인덱스를, 마지막 인자 $elems는 교체할 요소 개수를 지정해요. 예를 들어 인덱스 3의 요소 하나를 100, 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)␤»

$elems 인자를 지정하지 않으면 지정한 인덱스 $from에서 뒤쪽 요소를 모두 제거하며 치환이 일어나요.

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

$from 인자를 지정하지 않으면 버퍼의 맨 처음부터 치환이 일어나요.

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

routine subbuf-rw

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

버퍼의 일부분에 대한 쓰기 가능한 참조를 반환해요. 지정된 Buf에서 subbuf-rw 메서드를 호출합니다.

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

method reallocate

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

Buf의 요소 개수를 바꾸고, 바뀐 Buf를 반환해요. $elems 수에 맞춰 크기가 조정되는데, 실제 크기보다 작으면 줄어들고 크면 맞춰 늘어납니다. Buf가 커지는 경우 새로 생긴 항목에는 가상 머신 특유의 null 값이 할당되므로, 값에 의존하면 안 돼요. 가상 머신마다 값이 달라질 수 있기 때문입니다.

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 );
say $bú.raku; # OUTPUT: «Buf.new(1,2,3,0,0,0,0,0,0,0)␤»

method list

multi method list(Buf:D:)

정수의 List를 반환해요.

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

method push

method push( $elems )

버퍼 끝에 요소를 추가해요.

my $bú = Buf.new( 1, 1, 2, 3, 5 );
$bú.push( 8 );
say $bú.raku; # OUTPUT: «Buf.new(1,1,2,3,5,8)␤»

method pop

method pop()

버퍼의 마지막 요소를 반환하고 제거해요.

my $bú = Buf.new( 1, 1, 2, 3, 5 );
say $bú.pop(); # OUTPUT: «5␤»
say $bú.raku;  # OUTPUT: «Buf.new(1,1,2,3)␤»

method append

method append( $elems )

버퍼 끝에 이어 붙여요.

my $bú = Buf.new( 1, 1, 2, 3, 5 );
$bú.append(9, 8, 7, 6);
say $bú.raku; # OUTPUT: «Buf.new(1,1,2,3,5,9,8,7,6)␤»

method prepend

method prepend( $elems )

버퍼 맨 앞에 요소를 삽입해요.

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

unshift 메서드와의 차이는, 배열이나 리스트 인자를 하나만 prepend 하면 prepend는 그 배열/리스트를 펼치지만 unshift는 리스트/배열을 하나의 요소로 그대로 앞에 붙인다는 점이에요.

method shift

method shift()

버퍼의 첫 번째 요소를 제거하고 반환해요.

my $bú = Buf.new( 1, 1, 2, 3, 5 );
say $bú.shift(); # OUTPUT: «1␤»
say $bú.raku;    # OUTPUT: «Buf.new(1,2,3,5)␤»

method unshift

method unshift()

버퍼 맨 앞에 요소를 삽입해요.

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

method splice

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

버퍼의 요소들을 다른 요소들로 치환하고, 제거된 요소들을 담은 버퍼를 반환해요.

my $bú = Buf.new( 1, 1, 2, 3, 5 );
say $bú.splice:  0, 3, <3 2 1>;  # OUTPUT: «Buf:0x<01 01 02>␤»
say $bú.raku;                    # OUTPUT: «Buf.new(3,2,1,3,5)␤»

buf8에만 있는 메서드 (6.d, 2018.12 이후)

이 메서드들은 buf8 타입에서만 사용할 수 있어요. 바탕 데이터에 바이트를 쓰는 저수준 접근을 제공하는데, 타입(정수 또는 부동소수점(num)), 크기(8·16·32·64·128비트), 부호 여부(정수 값의 경우), 엔디언(네이티브·리틀·빅) 면에서 다양한 방식으로 쓸 수 있어요. 이 메서드들은 항상 Nil을 반환합니다.

엔디언은 이 메서드들의 세 번째 매개변수로 Endian 열거형 값을 사용해 지정해야 해요. 엔디언을 지정하지 않으면 NativeEndian이 가정됩니다. 그 밖의 값은 LittleEndianBigEndian이에요.

버퍼가 쓰는 바이트를 담기에 충분히 크지 않으면 자동으로 크기가 조정됩니다.

method write-uint8

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

주어진 위치에 부호 없는 8비트 정수 값을 써요. $endian 매개변수는 의미가 없지만 일관성을 위해 제공됩니다.

method write-int8

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

주어진 위치에 부호 있는 8비트 정수 값을 써요. $endian 매개변수는 의미가 없지만 일관성을 위해 제공됩니다.

method write-uint16

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

주어진 위치에 주어진 엔디언으로 부호 없는 16비트 정수 값을 써요.

method write-int16

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

주어진 위치에 주어진 엔디언으로 부호 있는 16비트 정수 값을 써요.

method write-uint32

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

주어진 위치에 주어진 엔디언으로 부호 없는 32비트 정수 값을 써요.

method write-int32

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

주어진 위치에 주어진 엔디언으로 부호 있는 32비트 정수 값을 써요.

method write-uint64

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

주어진 위치에 주어진 엔디언으로 부호 없는 64비트 정수 값을 써요.

method write-int64

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

주어진 위치에 주어진 엔디언으로 부호 있는 64비트 정수 값을 써요.

method write-uint128

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

주어진 위치에 주어진 엔디언으로 부호 없는 128비트 정수 값을 써요.

method write-int128

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

주어진 위치에 주어진 엔디언으로 부호 있는 128비트 정수 값을 써요.

method write-num32

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

주어진 위치에 주어진 엔디언으로 네이티브 num32 IEEE 부동소수점 값을 써요.

method write-num64

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

주어진 위치에 주어진 엔디언으로 네이티브 num64 IEEE 부동소수점 값을 써요.

buf8에만 있는 메서드 (6.d, 2019.03 이후)

method write-ubits

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

주어진 비트 오프셋과 비트 수로, 부호 없는 정수 값을 비트들에 써요. 비트의 엔디언은 BigEndian이라고 가정해요. 항상 Nil을 반환합니다.

method write-bits

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

주어진 비트 오프셋과 비트 수로, 부호 있는 정수 값을 비트들에 써요. 비트의 엔디언은 BigEndian이라고 가정해요. 항상 Nil을 반환합니다.

buf8에만 있는 메서드 (6.d, 2019.10 이후)

이 메서드들은 buf8 타입에서만 사용할 수 있어요. 바탕 데이터에 바이트를 쓰는 저수준 접근을 제공하는데, 타입(정수 또는 부동소수점(num)), 크기(8·16·32·64·128비트), 부호 여부(정수 값의 경우), 엔디언(네이티브·리틀·빅) 면에서 다양한 방식으로 쓸 수 있어요.

이 메서드들은 buf8 타입 객체에서도 호출할 수 있는데, 그 경우 새 buf8 객체가 반환됩니다. 그 외에는 buf8 객체에서 연산을 쉽게 체이닝할 수 있도록 invocant를 반환해요. 버퍼가 쓰는 바이트를 담기에 충분히 크지 않으면 자동으로 크기가 조정됩니다.

엔디언은 이 메서드들의 세 번째 매개변수로 Endian 열거형 값을 사용해 지정해야 해요. 엔디언을 지정하지 않으면 NativeEndian이 가정됩니다. 그 밖의 값은 LittleEndianBigEndian이에요.

method write-uint8

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

주어진 위치에 부호 없는 8비트 정수 값을 써요. $endian 매개변수는 의미가 없지만 일관성을 위해 제공됩니다.

method write-int8

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

주어진 위치에 부호 있는 8비트 정수 값을 써요. $endian 매개변수는 의미가 없지만 일관성을 위해 제공됩니다.

method write-uint16

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

주어진 위치에 주어진 엔디언으로 부호 없는 16비트 정수 값을 써요.

method write-int16

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

주어진 위치에 주어진 엔디언으로 부호 있는 16비트 정수 값을 써요.

method write-uint32

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

주어진 위치에 주어진 엔디언으로 부호 없는 32비트 정수 값을 써요.

method write-int32

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

주어진 위치에 주어진 엔디언으로 부호 있는 32비트 정수 값을 써요.

method write-uint64

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

주어진 위치에 주어진 엔디언으로 부호 없는 64비트 정수 값을 써요.

method write-int64

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

주어진 위치에 주어진 엔디언으로 부호 있는 64비트 정수 값을 써요.

method write-uint128

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

주어진 위치에 주어진 엔디언으로 부호 없는 128비트 정수 값을 써요.

method write-int128

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

주어진 위치에 주어진 엔디언으로 부호 있는 128비트 정수 값을 써요.

method write-num32

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

주어진 위치에 주어진 엔디언으로 네이티브 num32 IEEE 부동소수점 값을 써요.

method write-num64

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

주어진 위치에 주어진 엔디언으로 네이티브 num64 IEEE 부동소수점 값을 써요.

method write-ubits

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

주어진 비트 오프셋과 비트 수로, 부호 없는 정수 값을 비트들에 써요. 비트의 엔디언은 BigEndian이라고 가정해요.

method write-bits

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

주어진 비트 오프셋과 비트 수로, 부호 있는 정수 값을 비트들에 써요. 비트의 엔디언은 BigEndian이라고 가정해요.

method Blob

method Buf(Buf:D: --> Blob:D)

2021.06 Rakudo 컴파일러 릴리즈부터 사용할 수 있어요. invocant를 변경 불가능한 Blob 객체로 강제 변환해요.