Skip to content

Commit bf92f35

Browse files
committed
[Feature #21796] unpack variant ^ that returns the current offset
1 parent d5af8d7 commit bf92f35

File tree

4 files changed

+80
-0
lines changed

4 files changed

+80
-0
lines changed

doc/language/packed_data.rdoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ These tables summarize the directives for packing and unpacking.
9898
@ | skip to the offset given by the length argument
9999
X | skip backward one byte
100100
x | skip forward one byte
101+
^ | return the current offset
101102

102103
== Packing and Unpacking
103104

@@ -720,3 +721,7 @@ for one byte in the input or output string.
720721
"\x00\x00\x02".unpack("CxC") # => [0, 2]
721722
"\x00\x00\x02".unpack("x3C") # => [nil]
722723
"\x00\x00\x02".unpack("x4C") # Raises ArgumentError
724+
725+
- <tt>'^'</tt> - Only for unpacking; the current position:
726+
727+
"foo\0\0\0".unpack("Z*C") # => ["foo", 6]

pack.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,6 +1520,10 @@ pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset)
15201520
s += len;
15211521
break;
15221522

1523+
case '^':
1524+
UNPACK_PUSH(SSIZET2NUM(s - RSTRING_PTR(str)));
1525+
break;
1526+
15231527
case 'P':
15241528
if (sizeof(char *) <= (size_t)(send - s)) {
15251529
VALUE tmp = Qnil;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# encoding: binary
2+
ruby_version_is "4.1" do
3+
require_relative '../../../spec_helper'
4+
require_relative '../fixtures/classes'
5+
require_relative 'shared/basic'
6+
7+
describe "String#unpack with format '^'" do
8+
it_behaves_like :string_unpack_basic, '^'
9+
it_behaves_like :string_unpack_no_platform, '^'
10+
11+
it "returns the current offset that start from 0" do
12+
"".unpack("^").should == [0]
13+
end
14+
15+
it "returns the current offset after the last decode ended" do
16+
"a".unpack("CC^").should == [97, nil, 1]
17+
end
18+
19+
it "returns the current offset that start from the given offset" do
20+
"abc".unpack("^", offset: 1).should == [1]
21+
end
22+
23+
it "returns the offset moved by 'X'" do
24+
"\x01\x02\x03\x04".unpack("C3X2^").should == [1, 2, 3, 1]
25+
end
26+
27+
it "returns the offset moved by 'x'" do
28+
"\x01\x02\x03\x04".unpack("Cx2^").should == [1, 3]
29+
end
30+
31+
it "returns the offset to the position the previous decode ended" do
32+
"foo".unpack("A4^").should == ["foo", 3]
33+
"foo".unpack("a4^").should == ["foo", 3]
34+
"foo".unpack("Z5^").should == ["foo", 3]
35+
end
36+
37+
it "returns the offset including truncated part" do
38+
"foo ".unpack("A*^").should == ["foo", 6]
39+
"foo\0".unpack("Z*^").should == ["foo", 4]
40+
"foo\0\0\0".unpack("Z5^").should == ["foo", 5]
41+
end
42+
end
43+
end

test/ruby/test_pack.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,15 @@ def test_pack_unpack_aA
283283
assert_equal(["foo "], "foo ".unpack("a4"))
284284
assert_equal(["foo"], "foo".unpack("A4"))
285285
assert_equal(["foo"], "foo".unpack("a4"))
286+
287+
assert_equal(["foo", 4], "foo\0 ".unpack("A4^"))
288+
assert_equal(["foo\0", 4], "foo\0 ".unpack("a4^"))
289+
assert_equal(["foo", 4], "foo ".unpack("A4^"))
290+
assert_equal(["foo ", 4], "foo ".unpack("a4^"))
291+
assert_equal(["foo", 3], "foo".unpack("A4^"))
292+
assert_equal(["foo", 3], "foo".unpack("a4^"))
293+
assert_equal(["foo", 6], "foo\0 ".unpack("A*^"))
294+
assert_equal(["foo", 6], "foo ".unpack("A*^"))
286295
end
287296

288297
def test_pack_unpack_Z
@@ -298,6 +307,11 @@ def test_pack_unpack_Z
298307
assert_equal(["foo"], "foo".unpack("Z*"))
299308
assert_equal(["foo"], "foo\0".unpack("Z*"))
300309
assert_equal(["foo"], "foo".unpack("Z5"))
310+
311+
assert_equal(["foo", 3], "foo".unpack("Z*^"))
312+
assert_equal(["foo", 4], "foo\0".unpack("Z*^"))
313+
assert_equal(["foo", 3], "foo".unpack("Z5^"))
314+
assert_equal(["foo", 5], "foo\0\0\0".unpack("Z5^"))
301315
end
302316

303317
def test_pack_unpack_bB
@@ -549,6 +563,8 @@ def test_pack_unpack_x
549563

550564
assert_equal([0, 2], "\x00\x00\x02".unpack("CxC"))
551565
assert_raise(ArgumentError) { "".unpack("x") }
566+
567+
assert_equal([0, 1, 2, 2, 3], "\x00\x00\x02".unpack("C^x^C^"))
552568
end
553569

554570
def test_pack_unpack_X
@@ -558,6 +574,7 @@ def test_pack_unpack_X
558574

559575
assert_equal([0, 2, 2], "\x00\x02".unpack("CCXC"))
560576
assert_raise(ArgumentError) { "".unpack("X") }
577+
assert_equal([0, 1, 2, 2, 1, 2, 2], "\x00\x02".unpack("C^C^X^C^"))
561578
end
562579

563580
def test_pack_unpack_atmark
@@ -571,6 +588,17 @@ def test_pack_unpack_atmark
571588

572589
pos = RbConfig::LIMITS["UINTPTR_MAX"] - 99 # -100
573590
assert_raise(RangeError) {"0123456789".unpack("@#{pos}C10")}
591+
592+
assert_equal([1, 3, 4], "\x01\x00\x00\x02".unpack("x^@3^x^"))
593+
end
594+
595+
def test_unpack_carret
596+
assert_equal([0], "abc".unpack("^"))
597+
assert_equal([2], "abc".unpack("^", offset: 2))
598+
assert_equal([97, nil, 1], "a".unpack("CC^"))
599+
600+
assert_raise(ArgumentError) { "".unpack("^!") }
601+
assert_raise(ArgumentError) { "".unpack("^_") }
574602
end
575603

576604
def test_pack_unpack_percent

0 commit comments

Comments
 (0)