Skip to content

Commit eb053e7

Browse files
committed
[Feature #21979] Allow negative offset in unpack
1 parent 793f4c9 commit eb053e7

4 files changed

Lines changed: 43 additions & 12 deletions

File tree

pack.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,9 +1024,10 @@ pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset)
10241024
StringValue(fmt);
10251025
rb_must_asciicompat(fmt);
10261026

1027-
if (offset < 0) rb_raise(rb_eArgError, "offset can't be negative");
10281027
len = RSTRING_LEN(str);
1029-
if (offset > len) rb_raise(rb_eArgError, "offset outside of string");
1028+
if (offset < 0 ? (offset += len) < 0 : offset > len) {
1029+
rb_raise(rb_eArgError, "offset outside of string");
1030+
}
10301031

10311032
s = RSTRING_PTR(str);
10321033
send = s + len;

spec/ruby/core/string/unpack1_spec.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,22 @@
2020
"؈".unpack1("C", offset: 1).should == 136
2121
end
2222

23-
it "raises an ArgumentError when the offset is negative" do
24-
-> { "a".unpack1("C", offset: -1) }.should.raise(ArgumentError, "offset can't be negative")
23+
describe "when the offset is negative" do
24+
ruby_version_is "4.1" do
25+
it "starts unpacking from the end" do
26+
"abc".unpack1("C", offset: -2).should == 98
27+
end
28+
29+
it "raises an ArgumentError if it is less than -length" do
30+
-> { "a".unpack1("C", offset: -2) }.should.raise(ArgumentError, "offset outside of string")
31+
end
32+
end
33+
34+
ruby_version_is ""..."4.1" do
35+
it "raises an ArgumentError" do
36+
-> { "a".unpack1("C", offset: -1) }.should.raise(ArgumentError, "offset can't be negative")
37+
end
38+
end
2539
end
2640

2741
it "returns nil if the offset is at the end of the string" do

spec/ruby/core/string/unpack_spec.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,22 @@
1818
"؈".unpack("CC", offset: 1).should == [136, nil]
1919
end
2020

21-
it "raises an ArgumentError when the offset is negative" do
22-
-> { "a".unpack("C", offset: -1) }.should.raise(ArgumentError, "offset can't be negative")
21+
describe "when the offset is negative" do
22+
ruby_version_is "4.1" do
23+
it "starts unpacking from the end" do
24+
"abc".unpack("CC", offset: -2).should == [98, 99]
25+
end
26+
27+
it "raises an ArgumentError if it is less than -length" do
28+
-> { "a".unpack("C", offset: -2) }.should.raise(ArgumentError, "offset outside of string")
29+
end
30+
end
31+
32+
ruby_version_is ""..."4.1" do
33+
it "raises an ArgumentError" do
34+
-> { "a".unpack("C", offset: -1) }.should.raise(ArgumentError, "offset can't be negative")
35+
end
36+
end
2337
end
2438

2539
it "returns nil if the offset is at the end of the string" do

test/ruby/test_pack.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -913,27 +913,29 @@ def test_unpack1
913913

914914
def test_unpack1_offset
915915
assert_equal 65, "ZA".unpack1("C", offset: 1)
916+
assert_equal 65, "ZA".unpack1("C", offset: -1)
916917
assert_equal "01000001", "YZA".unpack1("B*", offset: 2)
917918
assert_nil "abc".unpack1("C", offset: 3)
918-
assert_raise_with_message(ArgumentError, /offset can't be negative/) {
919-
"a".unpack1("C", offset: -1)
920-
}
921919
assert_raise_with_message(ArgumentError, /offset outside of string/) {
922920
"a".unpack1("C", offset: 2)
923921
}
922+
assert_raise_with_message(ArgumentError, /offset outside of string/) {
923+
"a".unpack1("C", offset: -2)
924+
}
924925
assert_nil "a".unpack1("C", offset: 1)
925926
end
926927

927928
def test_unpack_offset
928929
assert_equal [65], "ZA".unpack("C", offset: 1)
930+
assert_equal [65], "ZA".unpack("C", offset: -1)
929931
assert_equal ["01000001"], "YZA".unpack("B*", offset: 2)
930932
assert_equal [nil, nil, nil], "abc".unpack("CCC", offset: 3)
931-
assert_raise_with_message(ArgumentError, /offset can't be negative/) {
932-
"a".unpack("C", offset: -1)
933-
}
934933
assert_raise_with_message(ArgumentError, /offset outside of string/) {
935934
"a".unpack("C", offset: 2)
936935
}
936+
assert_raise_with_message(ArgumentError, /offset outside of string/) {
937+
"a".unpack("C", offset: -2)
938+
}
937939
assert_equal [nil], "a".unpack("C", offset: 1)
938940
end
939941

0 commit comments

Comments
 (0)