ruby/doc/packed_data.rdoc
2025-01-02 12:23:49 +09:00

707 lines
23 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

= Packed \Data
== Quick Reference
These tables summarize the directives for packing and unpacking.
=== For Integers
Directive | Meaning
--------------|---------------------------------------------------------------
C | 8-bit unsigned (unsigned char)
S | 16-bit unsigned, native endian (uint16_t)
L | 32-bit unsigned, native endian (uint32_t)
Q | 64-bit unsigned, native endian (uint64_t)
J | pointer width unsigned, native endian (uintptr_t)
c | 8-bit signed (signed char)
s | 16-bit signed, native endian (int16_t)
l | 32-bit signed, native endian (int32_t)
q | 64-bit signed, native endian (int64_t)
j | pointer width signed, native endian (intptr_t)
S_ S! | unsigned short, native endian
I I_ I! | unsigned int, native endian
L_ L! | unsigned long, native endian
Q_ Q! | unsigned long long, native endian
| (raises ArgumentError if the platform has no long long type)
J! | uintptr_t, native endian (same with J)
s_ s! | signed short, native endian
i i_ i! | signed int, native endian
l_ l! | signed long, native endian
q_ q! | signed long long, native endian
| (raises ArgumentError if the platform has no long long type)
j! | intptr_t, native endian (same with j)
S> s> S!> s!> | each the same as the directive without >, but big endian
L> l> L!> l!> | S> is the same as n
I!> i!> | L> is the same as N
Q> q> Q!> q!> |
J> j> J!> j!> |
S< s< S!< s!< | each the same as the directive without <, but little endian
L< l< L!< l!< | S< is the same as v
I!< i!< | L< is the same as V
Q< q< Q!< q!< |
J< j< J!< j!< |
n | 16-bit unsigned, network (big-endian) byte order
N | 32-bit unsigned, network (big-endian) byte order
v | 16-bit unsigned, VAX (little-endian) byte order
V | 32-bit unsigned, VAX (little-endian) byte order
U | UTF-8 character
w | BER-compressed integer
=== For Floats
Directive | Meaning
----------|--------------------------------------------------
D d | double-precision, native format
F f | single-precision, native format
E | double-precision, little-endian byte order
e | single-precision, little-endian byte order
G | double-precision, network (big-endian) byte order
g | single-precision, network (big-endian) byte order
=== For Strings
Directive | Meaning
----------|-----------------------------------------------------------------
A | arbitrary binary string (remove trailing nulls and ASCII spaces)
a | arbitrary binary string
Z | null-terminated string
B | bit string (MSB first)
b | bit string (LSB first)
H | hex string (high nibble first)
h | hex string (low nibble first)
u | UU-encoded string
M | quoted-printable, MIME encoding (see RFC2045)
m | base64 encoded string (RFC 2045) (default)
| (base64 encoded string (RFC 4648) if followed by 0)
P | pointer to a structure (fixed-length string)
p | pointer to a null-terminated string
=== Additional Directives for Packing
Directive | Meaning
----------|----------------------------------------------------------------
@ | moves to absolute position
X | back up a byte
x | null byte
=== Additional Directives for Unpacking
Directive | Meaning
----------|----------------------------------------------------------------
@ | skip to the offset given by the length argument
X | skip backward one byte
x | skip forward one byte
== Packing and Unpacking
Certain Ruby core methods deal with packing and unpacking data:
- Method Array#pack:
Formats each element in array +self+ into a binary string;
returns that string.
- Method String#unpack:
Extracts data from string +self+,
forming objects that become the elements of a new array;
returns that array.
- Method String#unpack1:
Does the same, but unpacks and returns only the first extracted object.
Each of these methods accepts a string +template+,
consisting of zero or more _directive_ characters,
each followed by zero or more _modifier_ characters.
Examples (directive <tt>'C'</tt> specifies 'unsigned character'):
[65].pack('C') # => "A" # One element, one directive.
[65, 66].pack('CC') # => "AB" # Two elements, two directives.
[65, 66].pack('C') # => "A" # Extra element is ignored.
[65].pack('') # => "" # No directives.
[65].pack('CC') # Extra directive raises ArgumentError.
'A'.unpack('C') # => [65] # One character, one directive.
'AB'.unpack('CC') # => [65, 66] # Two characters, two directives.
'AB'.unpack('C') # => [65] # Extra character is ignored.
'A'.unpack('CC') # => [65, nil] # Extra directive generates nil.
'AB'.unpack('') # => [] # No directives.
The string +template+ may contain any mixture of valid directives
(directive <tt>'c'</tt> specifies 'signed character'):
[65, -1].pack('cC') # => "A\xFF"
"A\xFF".unpack('cC') # => [65, 255]
The string +template+ may contain whitespace (which is ignored)
and comments, each of which begins with character <tt>'#'</tt>
and continues up to and including the next following newline:
[0,1].pack(" C #foo \n C ") # => "\x00\x01"
"\0\1".unpack(" C #foo \n C ") # => [0, 1]
Any directive may be followed by either of these modifiers:
- <tt>'*'</tt> - The directive is to be applied as many times as needed:
[65, 66].pack('C*') # => "AB"
'AB'.unpack('C*') # => [65, 66]
- Integer +count+ - The directive is to be applied +count+ times:
[65, 66].pack('C2') # => "AB"
[65, 66].pack('C3') # Raises ArgumentError.
'AB'.unpack('C2') # => [65, 66]
'AB'.unpack('C3') # => [65, 66, nil]
Note: Directives in <tt>%w[A a Z m]</tt> use +count+ differently;
see {String Directives}[rdoc-ref:packed_data.rdoc@String+Directives].
If elements don't fit the provided directive, only least significant bits are encoded:
[257].pack("C").unpack("C") # => [1]
== Packing Method
Method Array#pack accepts optional keyword argument
+buffer+ that specifies the target string (instead of a new string):
[65, 66].pack('C*', buffer: 'foo') # => "fooAB"
The method can accept a block:
# Packed string is passed to the block.
[65, 66].pack('C*') {|s| p s } # => "AB"
== Unpacking Methods
Methods String#unpack and String#unpack1 each accept
an optional keyword argument +offset+ that specifies an offset
into the string:
'ABC'.unpack('C*', offset: 1) # => [66, 67]
'ABC'.unpack1('C*', offset: 1) # => 66
Both methods can accept a block:
# Each unpacked object is passed to the block.
ret = []
"ABCD".unpack("C*") {|c| ret << c }
ret # => [65, 66, 67, 68]
# The single unpacked object is passed to the block.
'AB'.unpack1('C*') {|ele| p ele } # => 65
== \Integer Directives
Each integer directive specifies the packing or unpacking
for one element in the input or output array.
=== 8-Bit \Integer Directives
- <tt>'c'</tt> - 8-bit signed integer
(like C <tt>signed char</tt>):
[0, 1, 255].pack('c*') # => "\x00\x01\xFF"
s = [0, 1, -1].pack('c*') # => "\x00\x01\xFF"
s.unpack('c*') # => [0, 1, -1]
- <tt>'C'</tt> - 8-bit unsigned integer
(like C <tt>unsigned char</tt>):
[0, 1, 255].pack('C*') # => "\x00\x01\xFF"
s = [0, 1, -1].pack('C*') # => "\x00\x01\xFF"
s.unpack('C*') # => [0, 1, 255]
=== 16-Bit \Integer Directives
- <tt>'s'</tt> - 16-bit signed integer, native-endian
(like C <tt>int16_t</tt>):
[513, -514].pack('s*') # => "\x01\x02\xFE\xFD"
s = [513, 65022].pack('s*') # => "\x01\x02\xFE\xFD"
s.unpack('s*') # => [513, -514]
- <tt>'S'</tt> - 16-bit unsigned integer, native-endian
(like C <tt>uint16_t</tt>):
[513, -514].pack('S*') # => "\x01\x02\xFE\xFD"
s = [513, 65022].pack('S*') # => "\x01\x02\xFE\xFD"
s.unpack('S*') # => [513, 65022]
- <tt>'n'</tt> - 16-bit network integer, big-endian:
s = [0, 1, -1, 32767, -32768, 65535].pack('n*')
# => "\x00\x00\x00\x01\xFF\xFF\x7F\xFF\x80\x00\xFF\xFF"
s.unpack('n*')
# => [0, 1, 65535, 32767, 32768, 65535]
- <tt>'v'</tt> - 16-bit VAX integer, little-endian:
s = [0, 1, -1, 32767, -32768, 65535].pack('v*')
# => "\x00\x00\x01\x00\xFF\xFF\xFF\x7F\x00\x80\xFF\xFF"
s.unpack('v*')
# => [0, 1, 65535, 32767, 32768, 65535]
=== 32-Bit \Integer Directives
- <tt>'l'</tt> - 32-bit signed integer, native-endian
(like C <tt>int32_t</tt>):
s = [67305985, -50462977].pack('l*')
# => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC"
s.unpack('l*')
# => [67305985, -50462977]
- <tt>'L'</tt> - 32-bit unsigned integer, native-endian
(like C <tt>uint32_t</tt>):
s = [67305985, 4244504319].pack('L*')
# => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC"
s.unpack('L*')
# => [67305985, 4244504319]
- <tt>'N'</tt> - 32-bit network integer, big-endian:
s = [0,1,-1].pack('N*')
# => "\x00\x00\x00\x00\x00\x00\x00\x01\xFF\xFF\xFF\xFF"
s.unpack('N*')
# => [0, 1, 4294967295]
- <tt>'V'</tt> - 32-bit VAX integer, little-endian:
s = [0,1,-1].pack('V*')
# => "\x00\x00\x00\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF"
s.unpack('v*')
# => [0, 0, 1, 0, 65535, 65535]
=== 64-Bit \Integer Directives
- <tt>'q'</tt> - 64-bit signed integer, native-endian
(like C <tt>int64_t</tt>):
s = [578437695752307201, -506097522914230529].pack('q*')
# => "\x01\x02\x03\x04\x05\x06\a\b\xFF\xFE\xFD\xFC\xFB\xFA\xF9\xF8"
s.unpack('q*')
# => [578437695752307201, -506097522914230529]
- <tt>'Q'</tt> - 64-bit unsigned integer, native-endian
(like C <tt>uint64_t</tt>):
s = [578437695752307201, 17940646550795321087].pack('Q*')
# => "\x01\x02\x03\x04\x05\x06\a\b\xFF\xFE\xFD\xFC\xFB\xFA\xF9\xF8"
s.unpack('Q*')
# => [578437695752307201, 17940646550795321087]
=== Platform-Dependent \Integer Directives
- <tt>'i'</tt> - Platform-dependent width signed integer,
native-endian (like C <tt>int</tt>):
s = [67305985, -50462977].pack('i*')
# => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC"
s.unpack('i*')
# => [67305985, -50462977]
- <tt>'I'</tt> - Platform-dependent width unsigned integer,
native-endian (like C <tt>unsigned int</tt>):
s = [67305985, -50462977].pack('I*')
# => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC"
s.unpack('I*')
# => [67305985, 4244504319]
- <tt>'j'</tt> - Pointer-width signed integer, native-endian
(like C <tt>intptr_t</tt>):
s = [67305985, -50462977].pack('j*')
# => "\x01\x02\x03\x04\x00\x00\x00\x00\xFF\xFE\xFD\xFC\xFF\xFF\xFF\xFF"
s.unpack('j*')
# => [67305985, -50462977]
- <tt>'J'</tt> - Pointer-width unsigned integer, native-endian
(like C <tt>uintptr_t</tt>):
s = [67305985, 4244504319].pack('J*')
# => "\x01\x02\x03\x04\x00\x00\x00\x00\xFF\xFE\xFD\xFC\x00\x00\x00\x00"
s.unpack('J*')
# => [67305985, 4244504319]
=== Other \Integer Directives
- <tt>'U'</tt> - UTF-8 character:
s = [4194304].pack('U*')
# => "\xF8\x90\x80\x80\x80"
s.unpack('U*')
# => [4194304]
- <tt>'w'</tt> - BER-encoded integer
(see {BER encoding}[https://en.wikipedia.org/wiki/X.690#BER_encoding]):
s = [1073741823].pack('w*')
# => "\x83\xFF\xFF\xFF\x7F"
s.unpack('w*')
# => [1073741823]
=== Modifiers for \Integer Directives
For the following directives, <tt>'!'</tt> or <tt>'_'</tt> modifiers may be
suffixed as underlying platforms native size.
- <tt>'i'</tt>, <tt>'I'</tt> - C <tt>int</tt>, always native size.
- <tt>'s'</tt>, <tt>'S'</tt> - C <tt>short</tt>.
- <tt>'l'</tt>, <tt>'L'</tt> - C <tt>long</tt>.
- <tt>'q'</tt>, <tt>'Q'</tt> - C <tt>long long</tt>, if available.
- <tt>'j'</tt>, <tt>'J'</tt> - C <tt>intptr_t</tt>, always native size.
Native size modifiers are silently ignored for always native size directives.
The endian modifiers also may be suffixed in the directives above:
- <tt>'>'</tt> - Big-endian.
- <tt>'<'</tt> - Little-endian.
== \Float Directives
Each float directive specifies the packing or unpacking
for one element in the input or output array.
=== Single-Precision \Float Directives
- <tt>'F'</tt> or <tt>'f'</tt> - Native format:
s = [3.0].pack('F') # => "\x00\x00@@"
s.unpack('F') # => [3.0]
- <tt>'e'</tt> - Little-endian:
s = [3.0].pack('e') # => "\x00\x00@@"
s.unpack('e') # => [3.0]
- <tt>'g'</tt> - Big-endian:
s = [3.0].pack('g') # => "@@\x00\x00"
s.unpack('g') # => [3.0]
=== Double-Precision \Float Directives
- <tt>'D'</tt> or <tt>'d'</tt> - Native format:
s = [3.0].pack('D') # => "\x00\x00\x00\x00\x00\x00\b@"
s.unpack('D') # => [3.0]
- <tt>'E'</tt> - Little-endian:
s = [3.0].pack('E') # => "\x00\x00\x00\x00\x00\x00\b@"
s.unpack('E') # => [3.0]
- <tt>'G'</tt> - Big-endian:
s = [3.0].pack('G') # => "@\b\x00\x00\x00\x00\x00\x00"
s.unpack('G') # => [3.0]
A float directive may be infinity or not-a-number:
inf = 1.0/0.0 # => Infinity
[inf].pack('f') # => "\x00\x00\x80\x7F"
"\x00\x00\x80\x7F".unpack('f') # => [Infinity]
nan = inf/inf # => NaN
[nan].pack('f') # => "\x00\x00\xC0\x7F"
"\x00\x00\xC0\x7F".unpack('f') # => [NaN]
== \String Directives
Each string directive specifies the packing or unpacking
for one byte in the input or output string.
=== Binary \String Directives
- <tt>'A'</tt> - Arbitrary binary string (space padded; count is width);
+nil+ is treated as the empty string:
['foo'].pack('A') # => "f"
['foo'].pack('A*') # => "foo"
['foo'].pack('A2') # => "fo"
['foo'].pack('A4') # => "foo "
[nil].pack('A') # => " "
[nil].pack('A*') # => ""
[nil].pack('A2') # => " "
[nil].pack('A4') # => " "
"foo\0".unpack('A') # => ["f"]
"foo\0".unpack('A4') # => ["foo"]
"foo\0bar".unpack('A10') # => ["foo\x00bar"] # Reads past "\0".
"foo ".unpack('A') # => ["f"]
"foo ".unpack('A4') # => ["foo"]
"foo".unpack('A4') # => ["foo"]
russian = "\u{442 435 441 442}" # => "тест"
russian.size # => 4
russian.bytesize # => 8
[russian].pack('A') # => "\xD1"
[russian].pack('A*') # => "\xD1\x82\xD0\xB5\xD1\x81\xD1\x82"
russian.unpack('A') # => ["\xD1"]
russian.unpack('A2') # => ["\xD1\x82"]
russian.unpack('A4') # => ["\xD1\x82\xD0\xB5"]
russian.unpack('A*') # => ["\xD1\x82\xD0\xB5\xD1\x81\xD1\x82"]
- <tt>'a'</tt> - Arbitrary binary string (null padded; count is width):
["foo"].pack('a') # => "f"
["foo"].pack('a*') # => "foo"
["foo"].pack('a2') # => "fo"
["foo\0"].pack('a4') # => "foo\x00"
[nil].pack('a') # => "\x00"
[nil].pack('a*') # => ""
[nil].pack('a2') # => "\x00\x00"
[nil].pack('a4') # => "\x00\x00\x00\x00"
"foo\0".unpack('a') # => ["f"]
"foo\0".unpack('a4') # => ["foo\x00"]
"foo ".unpack('a4') # => ["foo "]
"foo".unpack('a4') # => ["foo"]
"foo\0bar".unpack('a4') # => ["foo\x00"] # Reads past "\0".
- <tt>'Z'</tt> - Same as <tt>'a'</tt>,
except that null is added or ignored with <tt>'*'</tt>:
["foo"].pack('Z*') # => "foo\x00"
[nil].pack('Z*') # => "\x00"
"foo\0".unpack('Z*') # => ["foo"]
"foo".unpack('Z*') # => ["foo"]
"foo\0bar".unpack('Z*') # => ["foo"] # Does not read past "\0".
=== Bit \String Directives
- <tt>'B'</tt> - Bit string (high byte first):
['11111111' + '00000000'].pack('B*') # => "\xFF\x00"
['10000000' + '01000000'].pack('B*') # => "\x80@"
['1'].pack('B0') # => ""
['1'].pack('B1') # => "\x80"
['1'].pack('B2') # => "\x80\x00"
['1'].pack('B3') # => "\x80\x00"
['1'].pack('B4') # => "\x80\x00\x00"
['1'].pack('B5') # => "\x80\x00\x00"
['1'].pack('B6') # => "\x80\x00\x00\x00"
"\xff\x00".unpack("B*") # => ["1111111100000000"]
"\x01\x02".unpack("B*") # => ["0000000100000010"]
"".unpack("B0") # => [""]
"\x80".unpack("B1") # => ["1"]
"\x80".unpack("B2") # => ["10"]
"\x80".unpack("B3") # => ["100"]
- <tt>'b'</tt> - Bit string (low byte first):
['11111111' + '00000000'].pack('b*') # => "\xFF\x00"
['10000000' + '01000000'].pack('b*') # => "\x01\x02"
['1'].pack('b0') # => ""
['1'].pack('b1') # => "\x01"
['1'].pack('b2') # => "\x01\x00"
['1'].pack('b3') # => "\x01\x00"
['1'].pack('b4') # => "\x01\x00\x00"
['1'].pack('b5') # => "\x01\x00\x00"
['1'].pack('b6') # => "\x01\x00\x00\x00"
"\xff\x00".unpack("b*") # => ["1111111100000000"]
"\x01\x02".unpack("b*") # => ["1000000001000000"]
"".unpack("b0") # => [""]
"\x01".unpack("b1") # => ["1"]
"\x01".unpack("b2") # => ["10"]
"\x01".unpack("b3") # => ["100"]
=== Hex \String Directives
- <tt>'H'</tt> - Hex string (high nibble first):
['10ef'].pack('H*') # => "\x10\xEF"
['10ef'].pack('H0') # => ""
['10ef'].pack('H3') # => "\x10\xE0"
['10ef'].pack('H5') # => "\x10\xEF\x00"
['fff'].pack('H3') # => "\xFF\xF0"
['fff'].pack('H4') # => "\xFF\xF0"
['fff'].pack('H5') # => "\xFF\xF0\x00"
['fff'].pack('H6') # => "\xFF\xF0\x00"
['fff'].pack('H7') # => "\xFF\xF0\x00\x00"
['fff'].pack('H8') # => "\xFF\xF0\x00\x00"
"\x10\xef".unpack('H*') # => ["10ef"]
"\x10\xef".unpack('H0') # => [""]
"\x10\xef".unpack('H1') # => ["1"]
"\x10\xef".unpack('H2') # => ["10"]
"\x10\xef".unpack('H3') # => ["10e"]
"\x10\xef".unpack('H4') # => ["10ef"]
"\x10\xef".unpack('H5') # => ["10ef"]
- <tt>'h'</tt> - Hex string (low nibble first):
['10ef'].pack('h*') # => "\x01\xFE"
['10ef'].pack('h0') # => ""
['10ef'].pack('h3') # => "\x01\x0E"
['10ef'].pack('h5') # => "\x01\xFE\x00"
['fff'].pack('h3') # => "\xFF\x0F"
['fff'].pack('h4') # => "\xFF\x0F"
['fff'].pack('h5') # => "\xFF\x0F\x00"
['fff'].pack('h6') # => "\xFF\x0F\x00"
['fff'].pack('h7') # => "\xFF\x0F\x00\x00"
['fff'].pack('h8') # => "\xFF\x0F\x00\x00"
"\x01\xfe".unpack('h*') # => ["10ef"]
"\x01\xfe".unpack('h0') # => [""]
"\x01\xfe".unpack('h1') # => ["1"]
"\x01\xfe".unpack('h2') # => ["10"]
"\x01\xfe".unpack('h3') # => ["10e"]
"\x01\xfe".unpack('h4') # => ["10ef"]
"\x01\xfe".unpack('h5') # => ["10ef"]
=== Pointer \String Directives
- <tt>'P'</tt> - Pointer to a structure (fixed-length string):
s = ['abc'].pack('P') # => "\xE0O\x7F\xE5\xA1\x01\x00\x00"
s.unpack('P*') # => ["abc"]
".".unpack("P") # => []
("\0" * 8).unpack("P") # => [nil]
[nil].pack("P") # => "\x00\x00\x00\x00\x00\x00\x00\x00"
- <tt>'p'</tt> - Pointer to a null-terminated string:
s = ['abc'].pack('p') # => "(\xE4u\xE5\xA1\x01\x00\x00"
s.unpack('p*') # => ["abc"]
".".unpack("p") # => []
("\0" * 8).unpack("p") # => [nil]
[nil].pack("p") # => "\x00\x00\x00\x00\x00\x00\x00\x00"
=== Other \String Directives
- <tt>'M'</tt> - Quoted printable, MIME encoding;
text mode, but input must use LF and output LF;
(see {RFC 2045}[https://www.ietf.org/rfc/rfc2045.txt]):
["a b c\td \ne"].pack('M') # => "a b c\td =\n\ne=\n"
["\0"].pack('M') # => "=00=\n"
["a"*1023].pack('M') == ("a"*73+"=\n")*14+"a=\n" # => true
("a"*73+"=\na=\n").unpack('M') == ["a"*74] # => true
(("a"*73+"=\n")*14+"a=\n").unpack('M') == ["a"*1023] # => true
"a b c\td =\n\ne=\n".unpack('M') # => ["a b c\td \ne"]
"=00=\n".unpack('M') # => ["\x00"]
"pre=31=32=33after".unpack('M') # => ["pre123after"]
"pre=\nafter".unpack('M') # => ["preafter"]
"pre=\r\nafter".unpack('M') # => ["preafter"]
"pre=".unpack('M') # => ["pre="]
"pre=\r".unpack('M') # => ["pre=\r"]
"pre=hoge".unpack('M') # => ["pre=hoge"]
"pre==31after".unpack('M') # => ["pre==31after"]
"pre===31after".unpack('M') # => ["pre===31after"]
- <tt>'m'</tt> - Base64 encoded string;
count specifies input bytes between each newline,
rounded down to nearest multiple of 3;
if count is zero, no newlines are added;
(see {RFC 4648}[https://www.ietf.org/rfc/rfc4648.txt]):
[""].pack('m') # => ""
["\0"].pack('m') # => "AA==\n"
["\0\0"].pack('m') # => "AAA=\n"
["\0\0\0"].pack('m') # => "AAAA\n"
["\377"].pack('m') # => "/w==\n"
["\377\377"].pack('m') # => "//8=\n"
["\377\377\377"].pack('m') # => "////\n"
"".unpack('m') # => [""]
"AA==\n".unpack('m') # => ["\x00"]
"AAA=\n".unpack('m') # => ["\x00\x00"]
"AAAA\n".unpack('m') # => ["\x00\x00\x00"]
"/w==\n".unpack('m') # => ["\xFF"]
"//8=\n".unpack('m') # => ["\xFF\xFF"]
"////\n".unpack('m') # => ["\xFF\xFF\xFF"]
"A\n".unpack('m') # => [""]
"AA\n".unpack('m') # => ["\x00"]
"AA=\n".unpack('m') # => ["\x00"]
"AAA\n".unpack('m') # => ["\x00\x00"]
[""].pack('m0') # => ""
["\0"].pack('m0') # => "AA=="
["\0\0"].pack('m0') # => "AAA="
["\0\0\0"].pack('m0') # => "AAAA"
["\377"].pack('m0') # => "/w=="
["\377\377"].pack('m0') # => "//8="
["\377\377\377"].pack('m0') # => "////"
"".unpack('m0') # => [""]
"AA==".unpack('m0') # => ["\x00"]
"AAA=".unpack('m0') # => ["\x00\x00"]
"AAAA".unpack('m0') # => ["\x00\x00\x00"]
"/w==".unpack('m0') # => ["\xFF"]
"//8=".unpack('m0') # => ["\xFF\xFF"]
"////".unpack('m0') # => ["\xFF\xFF\xFF"]
- <tt>'u'</tt> - UU-encoded string:
[""].pack("u") # => ""
["a"].pack("u") # => "!80``\n"
["aaa"].pack("u") # => "#86%A\n"
"".unpack("u") # => [""]
"#86)C\n".unpack("u") # => ["abc"]
== Offset Directives
- <tt>'@'</tt> - Begin packing at the given byte offset;
for packing, null fill or shrink if necessary:
[1, 2].pack("C@0C") # => "\x02"
[1, 2].pack("C@1C") # => "\x01\x02"
[1, 2].pack("C@5C") # => "\x01\x00\x00\x00\x00\x02"
[*1..5].pack("CCCC@2C") # => "\x01\x02\x05"
For unpacking, cannot to move to outside the string:
"\x01\x00\x00\x02".unpack("C@3C") # => [1, 2]
"\x00".unpack("@1C") # => [nil]
"\x00".unpack("@2C") # Raises ArgumentError.
- <tt>'X'</tt> - For packing, shrink for the given byte offset:
[0, 1, 2].pack("CCXC") # => "\x00\x02"
[0, 1, 2].pack("CCX2C") # => "\x02"
For unpacking; rewind unpacking position for the given byte offset:
"\x00\x02".unpack("CCXC") # => [0, 2, 2]
Cannot to move to outside the string:
[0, 1, 2].pack("CCX3C") # Raises ArgumentError.
"\x00\x02".unpack("CX3C") # Raises ArgumentError.
- <tt>'x'</tt> - Begin packing at after the given byte offset;
for packing, null fill if necessary:
[].pack("x0") # => ""
[].pack("x") # => "\x00"
[].pack("x8") # => "\x00\x00\x00\x00\x00\x00\x00\x00"
For unpacking, cannot to move to outside the string:
"\x00\x00\x02".unpack("CxC") # => [0, 2]
"\x00\x00\x02".unpack("x3C") # => [nil]
"\x00\x00\x02".unpack("x4C") # Raises ArgumentError