Blob — 이진 데이터의 불변 인터페이스
Blob — 이진 데이터의 불변 인터페이스
이진 데이터를 다룰 때, 바이트들이 정수 리스트처럼 보이면 편할 때가 있어요. Blob 역할(role)이 바로 그런 인터페이스를 제공해요. 보통 부호 없는 정수들로 이루어진 리스트처럼 동작하는, 이진 타입의 불변 인터페이스예요. Positional[T]와 Stringy를 구현해요.
role Blob[::T = uint8] does Positional[T] does Stringy { }
매개변수화 타입이라서 여러 정수 타입으로 인스턴스화할 수 있어요.
my $b = Blob[int32].new(3, -3, 0xff32, -44);
say $b; # OUTPUT: «Blob[int32]:0x<03 -3 FF32 -2C>»
본문
기본적으로 Blob는 8비트 부호 없는 정수, 즉 Blob[uint8]과 같아요. 자주 쓰이는 몇 가지 Blob 타입은 고유한 클래스 이름을 갖고 있어요.
- blob8 | Blob[uint8]
- blob16 | Blob[uint16]
- blob32 | Blob[uint32]
- blob64 | Blob[uint64]
Blob과 거의 같은 방식으로 쓸 수 있어요.
my $blob = blob8.new(3, 6, 254);
say $blob; # OUTPUT: «Blob[uint8]:0x<03 06 FE>»
메서드
method new
multi method new(Blob:)
multi method new(Blob: Blob:D $blob)
multi method new(Blob: int @values)
multi method new(Blob: @values)
multi method new(Blob: *@values)
빈 Blob, 다른 Blob으로부터, 또는 정수(로 강제될) 값 리스트로부터 새 Blob을 만들어요.
my $blob = Blob.new([1, 2, 3]);
say Blob.new(<1 2 3>); # OUTPUT: «Blob:0x<01 02 03>»
method Bool
multi method Bool(Blob:D:)
버퍼가 비어 있을 때만 False를 돌려줘요.
my $blob = Blob.new();
say $blob.Bool; # OUTPUT: «False»
$blob = Blob.new([1, 2, 3]);
say $blob.Bool; # OUTPUT: «True»
method Capture
method Capture(Blob:D:)
객체를 List로 변환하고, 그걸 다시 Capture로 강제해요.
method elems
multi method elems(Blob:D:)
버퍼의 요소 수를 돌려줘요.
my $blob = Blob.new([1, 2, 3]);
say $blob.elems; # OUTPUT: «3»
method bytes
method bytes(Blob:D: --> Int:D)
버퍼의 요소들이 사용하는 바이트 수를 돌려줘요.
say Blob.new([1, 2, 3]).bytes; # OUTPUT: «3»
say blob16.new([1, 2, 3]).bytes; # OUTPUT: «6»
say blob64.new([1, 2, 3]).bytes; # OUTPUT: «24»
method chars
method chars(Blob:D:)
payload로 chars를 담아 X::Buf::AsStr을 던져요.
method Str
multi method Str(Blob:D:)
payload로 Str을 담아 X::Buf::AsStr을 던져요. Str로 변환하려면 .decode를 써야 해요.
method Stringy
multi method Stringy(Blob:D:)
payload로 Stringy를 담아 X::Buf::AsStr을 던져요.
method decode
multi method decode(Blob:D: $encoding = self.encoding // "utf-8")
multi method decode(Blob:D: $encoding, Str :$replacement!,
Bool:D :$strict = False)
multi method decode(Blob:D: $encoding, Bool:D :$strict = False)
인코딩을 적용해 blob을 Str로 바꿔요. 기본 인코딩은 UTF-8이에요.
my Blob $blob = "string".encode('utf-8');
say $blob.decode('utf-8'); # OUTPUT: «string»
잘못된 utf-8이면 .decode가 X::AdHoc을 던져요. 관대한 utf-8 처리가 필요하면 utf8-c8을 쓰세요.
method list
multi method list(Blob:D:)
정수들의 List를 돌려줘요.
say "zipi".encode("ascii").list; # OUTPUT: «(122 105 112 105)»
method gist
method gist(Blob:D: --> Str:D)
Blob의 "gist"를 담은 문자열을 돌려줘요. 처음 100개 요소까지만 공백으로 구분해 나열하고, 요소가 100개를 넘으면 줄임표를 붙여요.
put Blob.new(1, 2, 3).gist; # OUTPUT: «Blob:0x<01 02 03>»
put Blob.new(1..2000).gist;
# OUTPUT:
# Blob:0x<01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15
# 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C
# 2D 2E 2F 30 31 32 33 34
# 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43
# 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A
# 5B 5C 5D 5E 5F 60 61 62 63 64 ...>
method subbuf
multi method subbuf(Int $from, Int $len = self.elems --> Blob:D)
multi method subbuf(Range $range --> Blob:D)
multi method subbuf(Blob:D: &From)
multi method subbuf(Blob:D: Int:D $From, &End)
multi method subbuf(Blob:D: &From, &End)
multi method subbuf(Blob:D: \from, Whatever)
multi method subbuf(Blob:D: \from, Numeric \length)
invocant 버퍼에서 $from 인덱스부터 시작해 $len개 요소(또는 버퍼가 짧으면 그보다 적게)를 잘라 새 버퍼를 만들어요.
say Blob.new(1..10).subbuf(2, 4); # OUTPUT: «Blob:0x<03 04 05 06>»
say Blob.new(1..10).subbuf(*-2); # OUTPUT: «Blob:0x<09 0a>⤇»
say Blob.new(1..10).subbuf(*-5,2); # OUTPUT: «Blob:0x<06 07>»
편의상 Range로 원하는 부분을 지정할 수도 있어요.
say Blob.new(1..10).subbuf(2..5); # OUTPUT: «Blob:0x<03 04 05 06>»
method allocate
multi method allocate(Blob:U: Int:D $elements)
multi method allocate(Blob:U: Int:D $elements, int $value)
multi method allocate(Blob:U: Int:D $elements, Int:D \value)
multi method allocate(Blob:U: Int:D $elements, Mu:D $got)
multi method allocate(Blob:U: Int:D $elements, int @values)
multi method allocate(Blob:U: Int:D $elements, Blob:D $blob)
multi method allocate(Blob:U: Int:D $elements, @values)
주어진 요소 수의 새 Blob 객체를 돌려줘요. 선택적으로 두 번째 인자로 Blob을 채울 패턴을 줄 수 있어요. 단일 (네이티브) 정수 값 또는 정수 값을 생성하는 Iterable(다른 Blob 포함)이 될 수 있고, Blob 전체를 채울 값이 부족하면 패턴이 반복돼요.
my Blob $b0 = Blob.allocate(10,0);
$b0.say; # OUTPUT: «Blob:0x<00 00 00 00 00 00 00 00 00 00>»
패턴이 일반 Mu 값이면 실패해요.
routine unpack
이 메서드는 실험적(experimental) 이라서 쓰려면 다음을 해야 해요:
use experimental :pack;
multi method unpack(Blob:D: Str:D $template)
multi method unpack(Blob:D: @template)
multi unpack(Blob:D \blob, Str:D $template)
multi unpack(Blob:D \blob, @template)
템플릿 문자열에 따라 blob에서 특징(feature)을 추출해 리스트로 돌려줘요. 템플릿 문자열은 ASCII 글자로 시작하는 단위들로 구성되고, 선택적으로 수량자(quantifier)가 따라와요. 수량자는 *(보통 "여기서 Blob의 나머지를 다 쓴다") 또는 양의 정수(+ 없이)가 될 수 있어요. 템플릿 단위 사이의 공백은 무시돼요. 유효한 템플릿 예: "A4 C n*", "A*".
인식되는 글자:
- A | 각 Blob 요소를 코드포인트로 매핑해 문자열 추출
- a | 'A'와 동일
- C | blob의 요소를 정수로 추출
- H | 16진 문자열 추출
- L | 네 요소를 추출해 단일 부호 없는 정수로 반환
- n | 두 요소를 "network"(BigEndian) 바이트 순서로 결합해 단일 정수로
- N | 네 요소를 "network"(BigEndian) 바이트 순서로 결합해 단일 정수로
- S | 두 요소를 추출해 단일 부호 없는 정수로 반환
- v | 'S'와 동일
- V | 'L'과 동일
- x | blob에서 요소를 버림(무시)
- Z | 'A'와 동일
예시:
use experimental :pack;
say Blob.new(1..10).unpack("C*");
# OUTPUT: «(1 2 3 4 5 6 7 8 9 10)»
sub pack
이 서브루틴은 실험적이라서 쓰려면 use experimental :pack;을 해야 해요.
multi pack(Str $template, *@items)
multi pack(@template, *@items)
템플릿에 따라 주어진 항목들을 압축하고, 압축된 바이트를 담은 버퍼를 돌려줘요. 자세한 내용은 unpack을 보세요.
method reverse
method reverse(Blob:D: --> Blob:D)
모든 요소를 역순으로 바꾼 Blob을 돌려줘요.
say Blob.new([1, 2, 3]).reverse; # OUTPUT: «Blob:0x<03 02 01>»
say blob16.new([2]).reverse; # OUTPUT: «Blob[uint16]:0x<02>»
say blob32.new([16, 32]).reverse; # OUTPUT: «Blob[uint32]:0x<20 10>»
blob8 전용 메서드 (6.d, 2018.12 이후)
이 메서드들은 blob8(그리고 buf8) 타입에서만 사용 가능해요. 기본 데이터에서 바이트를 읽어 타입(정수 또는 부동소수점(num)), 크기(8, 16, 32, 64, 128비트), 부호(정수 값의 경우), 엔디언(네이티브, 리틀, 빅) 관점에서 다르게 해석하는 저수준 접근을 제공해요. 반환 값은 가능하면 64비트 네이티브 값으로, 불가능하면 (큰) 정수 값으로 확장돼요.
엔디언은 Endian enum 값으로 이 메서드들의 두 번째 인자에 지정해야 해요. 지정하지 않으면 NativeEndian이 가정돼요. 그 외 값은 LittleEndian, BigEndian이에요.
read-uint8(blob8:D: uint $pos, $endian = NativeEndian --> uint)— 주어진 위치의 바이트에 대한 부호 없는 네이티브 정수 값.$endian은 의미가 없지만 일관성을 위해 제공.read-int8(blob8:D: uint $pos, $endian = NativeEndian --> int)— 주어진 위치의 바이트에 대한 네이티브int값.read-uint16(blob8:D: uint $pos, $endian = NativeEndian --> uint)— 주어진 위치에서 시작하는 두 바이트의uint값.read-int16(blob8:D: uint $pos, $endian = NativeEndian --> int)— 주어진 위치에서 시작하는 두 바이트의int값.read-uint32(blob8:D: uint $pos, $endian = NativeEndian --> uint)— 주어진 위치에서 시작하는 네 바이트의uint값.read-int32(blob8:D: uint $pos, $endian = NativeEndian --> int)— 주어진 위치에서 시작하는 네 바이트의int값.read-uint64(blob8:D: uint $pos, $endian = NativeEndian --> UInt:D)— 주어진 위치에서 시작하는 여덟 바이트의 부호 없는 정수 값.read-int64(blob8:D: uint $pos, $endian = NativeEndian --> int)— 주어진 위치에서 시작하는 여덟 바이트의 네이티브int값.read-uint128(blob8:D: uint $pos, $endian = NativeEndian --> UInt:D)— 주어진 위치에서 시작하는 열여섯 바이트의 부호 없는 정수 값.read-int128(blob8:D: uint $pos, $endian = NativeEndian --> Int:D)— 주어진 위치에서 시작하는 열여섯 바이트의 정수 값.read-num32(blob8:D: uint $pos, $endian = NativeEndian --> int)— 주어진 위치에서 시작하는 네 바이트의 네이티브num값.read-num64(blob8:D: uint $pos, $endian = NativeEndian --> int)— 주어진 위치에서 시작하는 여덟 바이트의 네이티브num값.
blob8 전용 메서드 (6.d, 2019.03 이후)
read-ubits(blob8:D: uint $pos, uint $bits --> UInt:D)— 주어진 비트 오프셋과 비트 수의 부호 없는 정수 값. 비트의 엔디언은BigEndian으로 가정.read-bits(blob8:D: uint $pos, uint $bits --> Int:D)— 주어진 비트 오프셋과 비트 수의 부호 있는 정수 값. 비트의 엔디언은BigEndian으로 가정.
method Buf
method Buf(Blob:D: --> Buf:D)
Rakudo 컴파일러 2021.06 릴리스부터 사용 가능해요. invocant를 변경 가능한 Buf 객체로 강제해요.