Skip to content

Commit 34cab37

Browse files
committed
[Feature #21979] Allow negative offset in unpack
1 parent 3319be0 commit 34cab37

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
@@ -1022,9 +1022,10 @@ pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset)
10221022
StringValue(fmt);
10231023
rb_must_asciicompat(fmt);
10241024

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

10291030
s = RSTRING_PTR(str);
10301031
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_error(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_error(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_error(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_error(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_error(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_error(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
@@ -900,27 +900,29 @@ def test_unpack1
900900

901901
def test_unpack1_offset
902902
assert_equal 65, "ZA".unpack1("C", offset: 1)
903+
assert_equal 65, "ZA".unpack1("C", offset: -1)
903904
assert_equal "01000001", "YZA".unpack1("B*", offset: 2)
904905
assert_nil "abc".unpack1("C", offset: 3)
905-
assert_raise_with_message(ArgumentError, /offset can't be negative/) {
906-
"a".unpack1("C", offset: -1)
907-
}
908906
assert_raise_with_message(ArgumentError, /offset outside of string/) {
909907
"a".unpack1("C", offset: 2)
910908
}
909+
assert_raise_with_message(ArgumentError, /offset outside of string/) {
910+
"a".unpack1("C", offset: -2)
911+
}
911912
assert_nil "a".unpack1("C", offset: 1)
912913
end
913914

914915
def test_unpack_offset
915916
assert_equal [65], "ZA".unpack("C", offset: 1)
917+
assert_equal [65], "ZA".unpack("C", offset: -1)
916918
assert_equal ["01000001"], "YZA".unpack("B*", offset: 2)
917919
assert_equal [nil, nil, nil], "abc".unpack("CCC", offset: 3)
918-
assert_raise_with_message(ArgumentError, /offset can't be negative/) {
919-
"a".unpack("C", offset: -1)
920-
}
921920
assert_raise_with_message(ArgumentError, /offset outside of string/) {
922921
"a".unpack("C", offset: 2)
923922
}
923+
assert_raise_with_message(ArgumentError, /offset outside of string/) {
924+
"a".unpack("C", offset: -2)
925+
}
924926
assert_equal [nil], "a".unpack("C", offset: 1)
925927
end
926928

0 commit comments

Comments
 (0)