python-pickle 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +3 -0
- data/.github/workflows/ruby.yml +27 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +14 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +20 -0
- data/README.md +149 -0
- data/Rakefile +13 -0
- data/gemspec.yml +25 -0
- data/lib/python/pickle/byte_array.rb +40 -0
- data/lib/python/pickle/deserializer.rb +595 -0
- data/lib/python/pickle/exceptions.rb +12 -0
- data/lib/python/pickle/instruction.rb +52 -0
- data/lib/python/pickle/instructions/add_items.rb +26 -0
- data/lib/python/pickle/instructions/append.rb +24 -0
- data/lib/python/pickle/instructions/appends.rb +26 -0
- data/lib/python/pickle/instructions/bin_bytes.rb +32 -0
- data/lib/python/pickle/instructions/bin_bytes8.rb +32 -0
- data/lib/python/pickle/instructions/bin_float.rb +29 -0
- data/lib/python/pickle/instructions/bin_get.rb +27 -0
- data/lib/python/pickle/instructions/bin_int1.rb +29 -0
- data/lib/python/pickle/instructions/bin_put.rb +29 -0
- data/lib/python/pickle/instructions/bin_string.rb +32 -0
- data/lib/python/pickle/instructions/bin_unicode.rb +32 -0
- data/lib/python/pickle/instructions/bin_unicode8.rb +32 -0
- data/lib/python/pickle/instructions/build.rb +24 -0
- data/lib/python/pickle/instructions/byte_array8.rb +32 -0
- data/lib/python/pickle/instructions/dict.rb +17 -0
- data/lib/python/pickle/instructions/dup.rb +24 -0
- data/lib/python/pickle/instructions/empty_dict.rb +26 -0
- data/lib/python/pickle/instructions/empty_list.rb +26 -0
- data/lib/python/pickle/instructions/empty_set.rb +26 -0
- data/lib/python/pickle/instructions/empty_tuple.rb +26 -0
- data/lib/python/pickle/instructions/ext1.rb +29 -0
- data/lib/python/pickle/instructions/ext2.rb +29 -0
- data/lib/python/pickle/instructions/ext4.rb +29 -0
- data/lib/python/pickle/instructions/float.rb +24 -0
- data/lib/python/pickle/instructions/frame.rb +29 -0
- data/lib/python/pickle/instructions/frozen_set.rb +26 -0
- data/lib/python/pickle/instructions/get.rb +27 -0
- data/lib/python/pickle/instructions/global.rb +62 -0
- data/lib/python/pickle/instructions/has_length_and_value.rb +58 -0
- data/lib/python/pickle/instructions/has_value.rb +50 -0
- data/lib/python/pickle/instructions/int.rb +24 -0
- data/lib/python/pickle/instructions/list.rb +24 -0
- data/lib/python/pickle/instructions/long.rb +24 -0
- data/lib/python/pickle/instructions/long1.rb +32 -0
- data/lib/python/pickle/instructions/long4.rb +32 -0
- data/lib/python/pickle/instructions/long_bin_get.rb +27 -0
- data/lib/python/pickle/instructions/mark.rb +24 -0
- data/lib/python/pickle/instructions/memoize.rb +26 -0
- data/lib/python/pickle/instructions/new_false.rb +24 -0
- data/lib/python/pickle/instructions/new_obj.rb +26 -0
- data/lib/python/pickle/instructions/new_obj_ex.rb +26 -0
- data/lib/python/pickle/instructions/new_true.rb +24 -0
- data/lib/python/pickle/instructions/next_buffer.rb +26 -0
- data/lib/python/pickle/instructions/none.rb +24 -0
- data/lib/python/pickle/instructions/pop.rb +24 -0
- data/lib/python/pickle/instructions/pop_mark.rb +24 -0
- data/lib/python/pickle/instructions/proto.rb +29 -0
- data/lib/python/pickle/instructions/put.rb +24 -0
- data/lib/python/pickle/instructions/readonly_buffer.rb +26 -0
- data/lib/python/pickle/instructions/reduce.rb +24 -0
- data/lib/python/pickle/instructions/set_item.rb +24 -0
- data/lib/python/pickle/instructions/set_items.rb +26 -0
- data/lib/python/pickle/instructions/short_bin_bytes.rb +32 -0
- data/lib/python/pickle/instructions/short_bin_string.rb +32 -0
- data/lib/python/pickle/instructions/short_bin_unicode.rb +32 -0
- data/lib/python/pickle/instructions/stack_global.rb +26 -0
- data/lib/python/pickle/instructions/stop.rb +24 -0
- data/lib/python/pickle/instructions/string.rb +24 -0
- data/lib/python/pickle/instructions/tuple.rb +24 -0
- data/lib/python/pickle/instructions/tuple1.rb +24 -0
- data/lib/python/pickle/instructions/tuple2.rb +24 -0
- data/lib/python/pickle/instructions/tuple3.rb +24 -0
- data/lib/python/pickle/protocol.rb +56 -0
- data/lib/python/pickle/protocol0.rb +399 -0
- data/lib/python/pickle/protocol1.rb +183 -0
- data/lib/python/pickle/protocol2.rb +229 -0
- data/lib/python/pickle/protocol3.rb +163 -0
- data/lib/python/pickle/protocol4.rb +285 -0
- data/lib/python/pickle/protocol5.rb +218 -0
- data/lib/python/pickle/py_class.rb +75 -0
- data/lib/python/pickle/py_object.rb +141 -0
- data/lib/python/pickle/tuple.rb +19 -0
- data/lib/python/pickle/version.rb +6 -0
- data/lib/python/pickle.rb +226 -0
- data/python-pickle.gemspec +62 -0
- data/spec/byte_array_spec.rb +54 -0
- data/spec/deserializer_spec.rb +1201 -0
- data/spec/fixtures/ascii_str_v3.pkl +0 -0
- data/spec/fixtures/ascii_str_v4.pkl +0 -0
- data/spec/fixtures/ascii_str_v5.pkl +0 -0
- data/spec/fixtures/bin_str_v0.pkl +3 -0
- data/spec/fixtures/bin_str_v1.pkl +0 -0
- data/spec/fixtures/bin_str_v2.pkl +0 -0
- data/spec/fixtures/bin_str_v3.pkl +0 -0
- data/spec/fixtures/bin_str_v4.pkl +0 -0
- data/spec/fixtures/bin_str_v5.pkl +0 -0
- data/spec/fixtures/bytearray_v0.pkl +10 -0
- data/spec/fixtures/bytearray_v1.pkl +0 -0
- data/spec/fixtures/bytearray_v2.pkl +0 -0
- data/spec/fixtures/bytearray_v3.pkl +0 -0
- data/spec/fixtures/bytearray_v4.pkl +0 -0
- data/spec/fixtures/bytearray_v5.pkl +0 -0
- data/spec/fixtures/class_v0.pkl +4 -0
- data/spec/fixtures/class_v1.pkl +0 -0
- data/spec/fixtures/class_v2.pkl +0 -0
- data/spec/fixtures/class_v3.pkl +0 -0
- data/spec/fixtures/class_v4.pkl +0 -0
- data/spec/fixtures/class_v5.pkl +0 -0
- data/spec/fixtures/dict_v0.pkl +6 -0
- data/spec/fixtures/dict_v1.pkl +0 -0
- data/spec/fixtures/dict_v2.pkl +0 -0
- data/spec/fixtures/dict_v3.pkl +0 -0
- data/spec/fixtures/dict_v4.pkl +0 -0
- data/spec/fixtures/dict_v5.pkl +0 -0
- data/spec/fixtures/escaped_str_v0.pkl +3 -0
- data/spec/fixtures/escaped_str_v1.pkl +0 -0
- data/spec/fixtures/escaped_str_v2.pkl +0 -0
- data/spec/fixtures/false_v0.pkl +2 -0
- data/spec/fixtures/false_v1.pkl +2 -0
- data/spec/fixtures/false_v2.pkl +1 -0
- data/spec/fixtures/false_v3.pkl +1 -0
- data/spec/fixtures/false_v4.pkl +1 -0
- data/spec/fixtures/false_v5.pkl +1 -0
- data/spec/fixtures/float_v0.pkl +2 -0
- data/spec/fixtures/float_v1.pkl +1 -0
- data/spec/fixtures/float_v2.pkl +1 -0
- data/spec/fixtures/float_v3.pkl +1 -0
- data/spec/fixtures/float_v4.pkl +0 -0
- data/spec/fixtures/float_v5.pkl +0 -0
- data/spec/fixtures/function_v0.pkl +4 -0
- data/spec/fixtures/function_v1.pkl +0 -0
- data/spec/fixtures/function_v2.pkl +0 -0
- data/spec/fixtures/function_v3.pkl +0 -0
- data/spec/fixtures/function_v4.pkl +0 -0
- data/spec/fixtures/function_v5.pkl +0 -0
- data/spec/fixtures/hex_str_v0.pkl +3 -0
- data/spec/fixtures/hex_str_v1.pkl +0 -0
- data/spec/fixtures/hex_str_v2.pkl +0 -0
- data/spec/fixtures/int_v0.pkl +2 -0
- data/spec/fixtures/int_v1.pkl +1 -0
- data/spec/fixtures/int_v2.pkl +1 -0
- data/spec/fixtures/int_v3.pkl +1 -0
- data/spec/fixtures/int_v4.pkl +1 -0
- data/spec/fixtures/int_v5.pkl +1 -0
- data/spec/fixtures/list_v0.pkl +7 -0
- data/spec/fixtures/list_v1.pkl +0 -0
- data/spec/fixtures/list_v2.pkl +0 -0
- data/spec/fixtures/list_v3.pkl +0 -0
- data/spec/fixtures/list_v4.pkl +0 -0
- data/spec/fixtures/list_v5.pkl +0 -0
- data/spec/fixtures/long_v0.pkl +2 -0
- data/spec/fixtures/long_v1.pkl +2 -0
- data/spec/fixtures/long_v2.pkl +0 -0
- data/spec/fixtures/long_v3.pkl +0 -0
- data/spec/fixtures/long_v4.pkl +0 -0
- data/spec/fixtures/long_v5.pkl +0 -0
- data/spec/fixtures/nested_dict_v0.pkl +12 -0
- data/spec/fixtures/nested_dict_v1.pkl +0 -0
- data/spec/fixtures/nested_dict_v2.pkl +0 -0
- data/spec/fixtures/nested_dict_v3.pkl +0 -0
- data/spec/fixtures/nested_dict_v4.pkl +0 -0
- data/spec/fixtures/nested_dict_v5.pkl +0 -0
- data/spec/fixtures/nested_list_v0.pkl +9 -0
- data/spec/fixtures/nested_list_v1.pkl +0 -0
- data/spec/fixtures/nested_list_v2.pkl +0 -0
- data/spec/fixtures/nested_list_v3.pkl +0 -0
- data/spec/fixtures/nested_list_v4.pkl +0 -0
- data/spec/fixtures/nested_list_v5.pkl +0 -0
- data/spec/fixtures/none_v0.pkl +1 -0
- data/spec/fixtures/none_v1.pkl +1 -0
- data/spec/fixtures/none_v2.pkl +1 -0
- data/spec/fixtures/none_v3.pkl +1 -0
- data/spec/fixtures/none_v4.pkl +1 -0
- data/spec/fixtures/none_v5.pkl +1 -0
- data/spec/fixtures/object_v0.pkl +19 -0
- data/spec/fixtures/object_v1.pkl +0 -0
- data/spec/fixtures/object_v2.pkl +0 -0
- data/spec/fixtures/object_v3.pkl +0 -0
- data/spec/fixtures/object_v4.pkl +0 -0
- data/spec/fixtures/object_v5.pkl +0 -0
- data/spec/fixtures/str_v0.pkl +3 -0
- data/spec/fixtures/str_v1.pkl +0 -0
- data/spec/fixtures/str_v2.pkl +0 -0
- data/spec/fixtures/str_v3.pkl +0 -0
- data/spec/fixtures/str_v4.pkl +0 -0
- data/spec/fixtures/str_v5.pkl +0 -0
- data/spec/fixtures/true_v0.pkl +2 -0
- data/spec/fixtures/true_v1.pkl +2 -0
- data/spec/fixtures/true_v2.pkl +1 -0
- data/spec/fixtures/true_v3.pkl +1 -0
- data/spec/fixtures/true_v4.pkl +1 -0
- data/spec/fixtures/true_v5.pkl +1 -0
- data/spec/fixtures/unicode_str_v0.pkl +3 -0
- data/spec/fixtures/unicode_str_v1.pkl +0 -0
- data/spec/fixtures/unicode_str_v2.pkl +0 -0
- data/spec/fixtures/unicode_str_v3.pkl +0 -0
- data/spec/fixtures/unicode_str_v4.pkl +0 -0
- data/spec/fixtures/unicode_str_v5.pkl +0 -0
- data/spec/generate_pickles2.py +41 -0
- data/spec/generate_pickles3.py +40 -0
- data/spec/integration/load/protocol0_spec.rb +258 -0
- data/spec/integration/load/protocol1_spec.rb +258 -0
- data/spec/integration/load/protocol2_spec.rb +258 -0
- data/spec/integration/load/protocol3_spec.rb +258 -0
- data/spec/integration/load/protocol4_spec.rb +258 -0
- data/spec/integration/load/protocol5_spec.rb +258 -0
- data/spec/integration/parse/protocol0_spec.rb +467 -0
- data/spec/integration/parse/protocol1_spec.rb +459 -0
- data/spec/integration/parse/protocol2_spec.rb +471 -0
- data/spec/integration/parse/protocol3_spec.rb +407 -0
- data/spec/integration/parse/protocol4_spec.rb +439 -0
- data/spec/integration/parse/protocol5_spec.rb +419 -0
- data/spec/pickle_spec.rb +163 -0
- data/spec/protocol0_read_instruction_examples.rb +211 -0
- data/spec/protocol0_spec.rb +445 -0
- data/spec/protocol1_read_instruction_examples.rb +156 -0
- data/spec/protocol1_spec.rb +59 -0
- data/spec/protocol2_read_instruction_examples.rb +135 -0
- data/spec/protocol2_spec.rb +128 -0
- data/spec/protocol3_read_instruction_examples.rb +29 -0
- data/spec/protocol3_spec.rb +32 -0
- data/spec/protocol4_read_instruction_examples.rb +142 -0
- data/spec/protocol4_spec.rb +58 -0
- data/spec/protocol5_spec.rb +68 -0
- data/spec/py_class_spec.rb +62 -0
- data/spec/py_object_spec.rb +149 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/tuple_spec.rb +18 -0
- metadata +325 -0
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for "Protocol1#read_instruction examples" do
|
4
|
+
context "when the opcode is 41" do
|
5
|
+
let(:io) { StringIO.new(41.chr) }
|
6
|
+
|
7
|
+
it "must return Python::Pickle::Instructions::EMPTY_TUPLE" do
|
8
|
+
expect(subject.read_instruction).to be(
|
9
|
+
Python::Pickle::Instructions::EMPTY_TUPLE
|
10
|
+
)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when the opcode is 71" do
|
15
|
+
let(:float) { 1234.5678 }
|
16
|
+
let(:packed) { [float].pack('G') }
|
17
|
+
|
18
|
+
let(:io) { StringIO.new("#{71.chr}#{packed}\n") }
|
19
|
+
|
20
|
+
it "must return a Python::Pickle::Instructions::BinFloat object" do
|
21
|
+
expect(subject.read_instruction).to eq(
|
22
|
+
Python::Pickle::Instructions::BinFloat.new(float)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when the opcode is 75" do
|
28
|
+
let(:int) { 0xff }
|
29
|
+
let(:io) { StringIO.new("#{75.chr}#{int.chr}") }
|
30
|
+
|
31
|
+
it "must return a Python::Pickle::Instructions::BinInt1 object" do
|
32
|
+
expect(subject.read_instruction).to eq(
|
33
|
+
Python::Pickle::Instructions::BinInt1.new(int)
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when the opcode is 84" do
|
39
|
+
let(:string) { "hello\0world".b }
|
40
|
+
let(:length) { string.bytesize }
|
41
|
+
let(:packed) { [length, string].pack('L<a*') }
|
42
|
+
let(:io) { StringIO.new("#{84.chr}#{packed}") }
|
43
|
+
|
44
|
+
it "must return a Python::Pickle::Instructions::BinString object" do
|
45
|
+
expect(subject.read_instruction).to eq(
|
46
|
+
Python::Pickle::Instructions::BinString.new(length,string)
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when the opcode is 85" do
|
52
|
+
let(:string) { "hello\0world".b }
|
53
|
+
let(:length) { string.bytesize }
|
54
|
+
let(:packed) { [length, string].pack('Ca*') }
|
55
|
+
let(:io) { StringIO.new("#{85.chr}#{packed}") }
|
56
|
+
|
57
|
+
it "must return a Python::Pickle::Instructions::ShortBinString object" do
|
58
|
+
expect(subject.read_instruction).to eq(
|
59
|
+
Python::Pickle::Instructions::ShortBinString.new(length,string)
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when the opcode is 88" do
|
65
|
+
let(:string) { "hello world \u1234" }
|
66
|
+
let(:length) { string.bytesize }
|
67
|
+
let(:packed) { [length, string].pack('L<a*') }
|
68
|
+
let(:io) { StringIO.new("#{88.chr}#{packed}") }
|
69
|
+
|
70
|
+
it "must return a Python::Pickle::Instructions::BinUnicode object" do
|
71
|
+
expect(subject.read_instruction).to eq(
|
72
|
+
Python::Pickle::Instructions::BinUnicode.new(length,string)
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "must set the encoding of the String to Encoding::UTF_8" do
|
77
|
+
expect(subject.read_instruction.value.encoding).to eq(
|
78
|
+
Encoding::UTF_8
|
79
|
+
)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "when the opcode is 93" do
|
84
|
+
let(:io) { StringIO.new(93.chr) }
|
85
|
+
|
86
|
+
it "must return Python::Pickle::Instructions::EMPTY_LIST" do
|
87
|
+
expect(subject.read_instruction).to be(
|
88
|
+
Python::Pickle::Instructions::EMPTY_LIST
|
89
|
+
)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "when the opcode is 101" do
|
94
|
+
let(:io) { StringIO.new(101.chr) }
|
95
|
+
|
96
|
+
it "must return Python::Pickle::Instructions::APPENDS" do
|
97
|
+
expect(subject.read_instruction).to be(
|
98
|
+
Python::Pickle::Instructions::APPENDS
|
99
|
+
)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "when the opcode is 104" do
|
104
|
+
let(:index) { 2 }
|
105
|
+
let(:io) { StringIO.new("#{104.chr}#{index.chr}".b) }
|
106
|
+
|
107
|
+
it "must return a Python::Pickle::Instructions::BinGet object" do
|
108
|
+
expect(subject.read_instruction).to eq(
|
109
|
+
Python::Pickle::Instructions::BinGet.new(index)
|
110
|
+
)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "when the opcode is 106" do
|
115
|
+
let(:index) { 2 }
|
116
|
+
let(:packed) { [index].pack('L<') }
|
117
|
+
let(:io) { StringIO.new("#{106.chr}#{packed}".b) }
|
118
|
+
|
119
|
+
it "must return a Python::Pickle::Instructions::LongBinGet object" do
|
120
|
+
expect(subject.read_instruction).to eq(
|
121
|
+
Python::Pickle::Instructions::LongBinGet.new(index)
|
122
|
+
)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when the opcode is 113" do
|
127
|
+
let(:index) { 2 }
|
128
|
+
let(:io) { StringIO.new("#{113.chr}#{index.chr}") }
|
129
|
+
|
130
|
+
it "must return a Python::Pickle::Instructions::BinPut object" do
|
131
|
+
expect(subject.read_instruction).to eq(
|
132
|
+
Python::Pickle::Instructions::BinPut.new(index)
|
133
|
+
)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "when the opcode is 117" do
|
138
|
+
let(:io) { StringIO.new(117.chr) }
|
139
|
+
|
140
|
+
it "must return Python::Pickle::Instructions::SETITEMS" do
|
141
|
+
expect(subject.read_instruction).to be(
|
142
|
+
Python::Pickle::Instructions::SETITEMS
|
143
|
+
)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context "when the opcode is 125" do
|
148
|
+
let(:io) { StringIO.new(125.chr) }
|
149
|
+
|
150
|
+
it "must return Python::Pickle::Instructions::EMPTY_DICT" do
|
151
|
+
expect(subject.read_instruction).to be(
|
152
|
+
Python::Pickle::Instructions::EMPTY_DICT
|
153
|
+
)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'python/pickle/protocol1'
|
3
|
+
|
4
|
+
require 'protocol0_read_instruction_examples'
|
5
|
+
require 'protocol1_read_instruction_examples'
|
6
|
+
|
7
|
+
describe Python::Pickle::Protocol1 do
|
8
|
+
let(:pickle) { '' }
|
9
|
+
let(:io) { StringIO.new(pickle) }
|
10
|
+
|
11
|
+
subject { described_class.new(io) }
|
12
|
+
|
13
|
+
describe "#read_float64_be" do
|
14
|
+
let(:float) { 1234.5678 }
|
15
|
+
let(:packed) { [float].pack('G') }
|
16
|
+
|
17
|
+
let(:io) { StringIO.new(packed) }
|
18
|
+
|
19
|
+
it "must read eight bytes and decode a big-endian floating point number" do
|
20
|
+
expect(subject.read_float64_be).to eq(float)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#read_uint8" do
|
25
|
+
let(:io) { StringIO.new("\xff".b) }
|
26
|
+
|
27
|
+
it "must read a single unsignd byte" do
|
28
|
+
expect(subject.read_uint8).to eq(0xff)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#read_uint32_le" do
|
33
|
+
let(:uint32) { 0xffeeddcc }
|
34
|
+
let(:packed) { [uint32].pack('L<') }
|
35
|
+
let(:io) { StringIO.new(packed) }
|
36
|
+
|
37
|
+
it "must read four bytes and return an unpacked uint32 in little-endian byte-order" do
|
38
|
+
expect(subject.read_uint32_le).to eq(uint32)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:fixtures_dir) { File.join(__dir__,'fixtures') }
|
43
|
+
|
44
|
+
describe "#read_instruction" do
|
45
|
+
include_context "Protocol0#read_instruction examples"
|
46
|
+
include_context "Protocol1#read_instruction examples"
|
47
|
+
|
48
|
+
context "when the opcode is not recognized" do
|
49
|
+
let(:opcode) { 255 }
|
50
|
+
let(:io) { StringIO.new(opcode.chr) }
|
51
|
+
|
52
|
+
it do
|
53
|
+
expect {
|
54
|
+
subject.read_instruction
|
55
|
+
}.to raise_error(Python::Pickle::InvalidFormat,"invalid opcode (#{opcode.inspect}) for protocol 1")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for "Protocol2#read_instruction examples" do
|
4
|
+
context "when the opcode is 128" do
|
5
|
+
let(:version) { 0x02 }
|
6
|
+
let(:io) { StringIO.new("#{128.chr}#{version.chr}") }
|
7
|
+
|
8
|
+
it "must return a Python::Pickle::Instructions::Proto object" do
|
9
|
+
expect(subject.read_instruction).to eq(
|
10
|
+
Python::Pickle::Instructions::Proto.new(version)
|
11
|
+
)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when the opcode is 129" do
|
16
|
+
let(:io) { StringIO.new(129.chr) }
|
17
|
+
|
18
|
+
it "must return Python::Pickle::Instructions::NEWOBJ" do
|
19
|
+
expect(subject.read_instruction).to be(
|
20
|
+
Python::Pickle::Instructions::NEWOBJ
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when the opcode is 130" do
|
26
|
+
let(:code) { 0xfe }
|
27
|
+
let(:io) { StringIO.new("#{130.chr}#{code.chr}") }
|
28
|
+
|
29
|
+
it "must return Python::Pickle::Instructions::Ext1" do
|
30
|
+
expect(subject.read_instruction).to eq(
|
31
|
+
Python::Pickle::Instructions::Ext1.new(code)
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when the opcode is 131" do
|
37
|
+
let(:code) { 0xfffe }
|
38
|
+
let(:packed) { [code].pack('S<') }
|
39
|
+
let(:io) { StringIO.new("#{131.chr}#{packed}") }
|
40
|
+
|
41
|
+
it "must return Python::Pickle::Instructions::Ext2" do
|
42
|
+
expect(subject.read_instruction).to eq(
|
43
|
+
Python::Pickle::Instructions::Ext2.new(code)
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when the opcode is 132" do
|
49
|
+
let(:code) { 0xfffffffe }
|
50
|
+
let(:packed) { [code].pack('L<') }
|
51
|
+
let(:io) { StringIO.new("#{132.chr}#{packed}") }
|
52
|
+
|
53
|
+
it "must return Python::Pickle::Instructions::Ext4" do
|
54
|
+
expect(subject.read_instruction).to eq(
|
55
|
+
Python::Pickle::Instructions::Ext4.new(code)
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when the opcode is 133" do
|
61
|
+
let(:io) { StringIO.new(133.chr) }
|
62
|
+
|
63
|
+
it "must return Python::Pickle::Instructions::TUPLE1" do
|
64
|
+
expect(subject.read_instruction).to be(
|
65
|
+
Python::Pickle::Instructions::TUPLE1
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when the opcode is 134" do
|
71
|
+
let(:io) { StringIO.new(134.chr) }
|
72
|
+
|
73
|
+
it "must return Python::Pickle::Instructions::TUPLE2" do
|
74
|
+
expect(subject.read_instruction).to be(
|
75
|
+
Python::Pickle::Instructions::TUPLE2
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when the opcode is 135" do
|
81
|
+
let(:io) { StringIO.new(135.chr) }
|
82
|
+
|
83
|
+
it "must return Python::Pickle::Instructions::TUPLE3" do
|
84
|
+
expect(subject.read_instruction).to be(
|
85
|
+
Python::Pickle::Instructions::TUPLE3
|
86
|
+
)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "when the opcode is 136" do
|
91
|
+
let(:io) { StringIO.new(136.chr) }
|
92
|
+
|
93
|
+
it "must return Python::Pickle::Instructions::NEWTRUE" do
|
94
|
+
expect(subject.read_instruction).to be(
|
95
|
+
Python::Pickle::Instructions::NEWTRUE
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "when the opcode is 137" do
|
101
|
+
let(:io) { StringIO.new(137.chr) }
|
102
|
+
|
103
|
+
it "must return Python::Pickle::Instructions::NEWFALSE" do
|
104
|
+
expect(subject.read_instruction).to be(
|
105
|
+
Python::Pickle::Instructions::NEWFALSE
|
106
|
+
)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "when the opcode is 138" do
|
111
|
+
let(:length) { 2 }
|
112
|
+
let(:uint) { 0x1234 }
|
113
|
+
let(:packed) { [length, uint].pack('CS<') }
|
114
|
+
let(:io) { StringIO.new("#{138.chr}#{packed}".b) }
|
115
|
+
|
116
|
+
it "must return Python::Pickle::Instructions::Long1" do
|
117
|
+
expect(subject.read_instruction).to eq(
|
118
|
+
Python::Pickle::Instructions::Long1.new(length,uint)
|
119
|
+
)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "when the opcode is 139" do
|
124
|
+
let(:length) { 4 }
|
125
|
+
let(:uint) { 0x12345678 }
|
126
|
+
let(:packed) { [length, uint].pack('L<L<') }
|
127
|
+
let(:io) { StringIO.new("#{139.chr}#{packed}".b) }
|
128
|
+
|
129
|
+
it "must return Python::Pickle::Instructions::Long4" do
|
130
|
+
expect(subject.read_instruction).to eq(
|
131
|
+
Python::Pickle::Instructions::Long4.new(length,uint)
|
132
|
+
)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'python/pickle/protocol2'
|
3
|
+
|
4
|
+
require 'protocol0_read_instruction_examples'
|
5
|
+
require 'protocol1_read_instruction_examples'
|
6
|
+
require 'protocol2_read_instruction_examples'
|
7
|
+
|
8
|
+
describe Python::Pickle::Protocol2 do
|
9
|
+
let(:pickle) { '' }
|
10
|
+
let(:io) { StringIO.new(pickle) }
|
11
|
+
|
12
|
+
subject { described_class.new(io) }
|
13
|
+
|
14
|
+
describe "#read_uint16_le" do
|
15
|
+
let(:uint16) { 0xfffe }
|
16
|
+
let(:packed) { [uint16].pack('S<') }
|
17
|
+
let(:io) { StringIO.new(packed) }
|
18
|
+
|
19
|
+
it "must read two bytes and return an unpacked unsigned 16bit integer" do
|
20
|
+
expect(subject.read_uint16_le).to eq(uint16)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#unpack_int_le" do
|
25
|
+
context "when given an empty String" do
|
26
|
+
it "must return 0" do
|
27
|
+
expect(subject.unpack_int_le("".b)).to eq(0)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when given a single byte String" do
|
32
|
+
it "must return the first byte" do
|
33
|
+
expect(subject.unpack_int_le("\x01".b)).to eq(0x01)
|
34
|
+
end
|
35
|
+
|
36
|
+
context "but the most significant bits are set" do
|
37
|
+
it "must return a negative integer" do
|
38
|
+
expect(subject.unpack_int_le("\xff".b)).to eq(-1)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when given a two byte string" do
|
44
|
+
it "must decode the string and return an integer" do
|
45
|
+
expect(subject.unpack_int_le("\x00\x01".b)).to eq(0x100)
|
46
|
+
end
|
47
|
+
|
48
|
+
context "but the most significant bits are set" do
|
49
|
+
it "must return a negative integer" do
|
50
|
+
expect(subject.unpack_int_le("\x00\xff".b)).to eq(-256)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when given a three byte string" do
|
56
|
+
it "must decode the string and return an integer" do
|
57
|
+
expect(subject.unpack_int_le("\x00\x00\xff".b)).to eq(-65536)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#read_int_le" do
|
63
|
+
context "when called with 0" do
|
64
|
+
let(:io) { StringIO.new("".b) }
|
65
|
+
|
66
|
+
it "must read zero bytes return 0" do
|
67
|
+
expect(subject.read_int_le(0)).to eq(0)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when called with 1" do
|
72
|
+
let(:io) { StringIO.new("\x01".b) }
|
73
|
+
|
74
|
+
it "must read one byte return the first byte" do
|
75
|
+
expect(subject.read_int_le(1)).to eq(0x01)
|
76
|
+
end
|
77
|
+
|
78
|
+
context "but the most significant bits are set" do
|
79
|
+
let(:io) { StringIO.new("\xff".b) }
|
80
|
+
|
81
|
+
it "must read one byte return a negative integer" do
|
82
|
+
expect(subject.read_int_le(1)).to eq(-1)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "when called with 2" do
|
88
|
+
let(:io) { StringIO.new("\x00\x01".b) }
|
89
|
+
|
90
|
+
it "must read two bytes and decode the string and return an integer" do
|
91
|
+
expect(subject.read_int_le(2)).to eq(0x100)
|
92
|
+
end
|
93
|
+
|
94
|
+
context "but the most significant bits are set" do
|
95
|
+
let(:io) { StringIO.new("\x00\xff".b) }
|
96
|
+
|
97
|
+
it "must read two bytes and return a negative integer" do
|
98
|
+
expect(subject.read_int_le(2)).to eq(-256)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "when called with 3" do
|
104
|
+
let(:io) { StringIO.new("\x00\x00\xff".b) }
|
105
|
+
|
106
|
+
it "must read three bytes and decode the string and return an integer" do
|
107
|
+
expect(subject.read_int_le(3)).to eq(-65536)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#read_instruction" do
|
113
|
+
include_context "Protocol0#read_instruction examples"
|
114
|
+
include_context "Protocol1#read_instruction examples"
|
115
|
+
include_context "Protocol2#read_instruction examples"
|
116
|
+
|
117
|
+
context "when the opcode is not recognized" do
|
118
|
+
let(:opcode) { 255 }
|
119
|
+
let(:io) { StringIO.new(opcode.chr) }
|
120
|
+
|
121
|
+
it do
|
122
|
+
expect {
|
123
|
+
subject.read_instruction
|
124
|
+
}.to raise_error(Python::Pickle::InvalidFormat,"invalid opcode (#{opcode.inspect}) for protocol 2")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for "Protocol3#read_instruction examples" do
|
4
|
+
describe "when the opcode is 66" do
|
5
|
+
let(:length) { 12 }
|
6
|
+
let(:bytes) { "hello world\0".b }
|
7
|
+
let(:packed) { [length, bytes].pack('L<a*') }
|
8
|
+
let(:io) { StringIO.new("#{66.chr}#{packed}".b) }
|
9
|
+
|
10
|
+
it "must return Python::Pickle::Instructions::BinBytes" do
|
11
|
+
expect(subject.read_instruction).to eq(
|
12
|
+
Python::Pickle::Instructions::BinBytes.new(length,bytes)
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "when the opcode is 67" do
|
18
|
+
let(:length) { 12 }
|
19
|
+
let(:bytes) { "hello world\0".b }
|
20
|
+
let(:packed) { [length, bytes].pack('Ca*') }
|
21
|
+
let(:io) { StringIO.new("#{67.chr}#{packed}".b) }
|
22
|
+
|
23
|
+
it "must return Python::Pickle::Instructions::ShortBinBytes" do
|
24
|
+
expect(subject.read_instruction).to eq(
|
25
|
+
Python::Pickle::Instructions::ShortBinBytes.new(length,bytes)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'python/pickle/protocol3'
|
3
|
+
|
4
|
+
require 'protocol0_read_instruction_examples'
|
5
|
+
require 'protocol1_read_instruction_examples'
|
6
|
+
require 'protocol2_read_instruction_examples'
|
7
|
+
require 'protocol3_read_instruction_examples'
|
8
|
+
|
9
|
+
describe Python::Pickle::Protocol3 do
|
10
|
+
let(:pickle) { '' }
|
11
|
+
let(:io) { StringIO.new(pickle) }
|
12
|
+
|
13
|
+
subject { described_class.new(io) }
|
14
|
+
|
15
|
+
describe "#read_instruction" do
|
16
|
+
include_context "Protocol0#read_instruction examples"
|
17
|
+
include_context "Protocol1#read_instruction examples"
|
18
|
+
include_context "Protocol2#read_instruction examples"
|
19
|
+
include_context "Protocol3#read_instruction examples"
|
20
|
+
|
21
|
+
context "when the opcode is not recognized" do
|
22
|
+
let(:opcode) { 255 }
|
23
|
+
let(:io) { StringIO.new(opcode.chr) }
|
24
|
+
|
25
|
+
it do
|
26
|
+
expect {
|
27
|
+
subject.read_instruction
|
28
|
+
}.to raise_error(Python::Pickle::InvalidFormat,"invalid opcode (#{opcode.inspect}) for protocol 3")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for "Protocol4#read_instruction examples" do
|
4
|
+
describe "when the opcode is 140" do
|
5
|
+
let(:string) { "hello world\u1234" }
|
6
|
+
let(:length) { string.bytesize }
|
7
|
+
let(:packed) { [length, *string.codepoints].pack('CU*') }
|
8
|
+
let(:io) { StringIO.new("#{140.chr}#{packed}".b) }
|
9
|
+
|
10
|
+
it "must return Python::Pickle::Instructions::ShortBinUnicode" do
|
11
|
+
expect(subject.read_instruction).to eq(
|
12
|
+
Python::Pickle::Instructions::ShortBinUnicode.new(length,string)
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "when the opcode is 141" do
|
18
|
+
let(:string) { "hello world\u1234" }
|
19
|
+
let(:length) { string.bytesize }
|
20
|
+
let(:packed) { [length, *string.codepoints].pack('Q<U*') }
|
21
|
+
let(:io) { StringIO.new("#{141.chr}#{packed}".b) }
|
22
|
+
|
23
|
+
it "must return Python::Pickle::Instructions::BinUnicode8" do
|
24
|
+
expect(subject.read_instruction).to eq(
|
25
|
+
Python::Pickle::Instructions::BinUnicode8.new(length,string)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "when the opcode is 142" do
|
31
|
+
let(:length) { 12 }
|
32
|
+
let(:bytes) { "hello world\0".b }
|
33
|
+
let(:packed) { [length, bytes].pack('Q<a*') }
|
34
|
+
let(:io) { StringIO.new("#{142.chr}#{packed}".b) }
|
35
|
+
|
36
|
+
it "must return Python::Pickle::Instructions::BinBytes8" do
|
37
|
+
expect(subject.read_instruction).to eq(
|
38
|
+
Python::Pickle::Instructions::BinBytes8.new(length,bytes)
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "when the opcode is 143" do
|
44
|
+
let(:io) { StringIO.new(143.chr) }
|
45
|
+
|
46
|
+
it "must return Python::Pickle::Instructions::EMPTY_SET" do
|
47
|
+
expect(subject.read_instruction).to be(
|
48
|
+
Python::Pickle::Instructions::EMPTY_SET
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "when the opcode is 144" do
|
54
|
+
let(:io) { StringIO.new(144.chr) }
|
55
|
+
|
56
|
+
it "must return Python::Pickle::Instructions::ADDITEMS" do
|
57
|
+
expect(subject.read_instruction).to be(
|
58
|
+
Python::Pickle::Instructions::ADDITEMS
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "when the opcode is 145" do
|
64
|
+
let(:io) { StringIO.new(145.chr) }
|
65
|
+
|
66
|
+
it "must return Python::Pickle::Instructions::FROZENSET" do
|
67
|
+
expect(subject.read_instruction).to be(
|
68
|
+
Python::Pickle::Instructions::FROZENSET
|
69
|
+
)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "when the opcode is 146" do
|
74
|
+
let(:io) { StringIO.new(146.chr) }
|
75
|
+
|
76
|
+
it "must return Python::Pickle::Instructions::NEWOBJ_EX" do
|
77
|
+
expect(subject.read_instruction).to be(
|
78
|
+
Python::Pickle::Instructions::NEWOBJ_EX
|
79
|
+
)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "when the opcode is 147" do
|
84
|
+
let(:io) { StringIO.new(147.chr) }
|
85
|
+
|
86
|
+
it "must return Python::Pickle::Instructions::STACK_GLOBAL" do
|
87
|
+
expect(subject.read_instruction).to be(
|
88
|
+
Python::Pickle::Instructions::STACK_GLOBAL
|
89
|
+
)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "when the opcode is 148" do
|
94
|
+
let(:io) { StringIO.new(148.chr) }
|
95
|
+
|
96
|
+
it "must return Python::Pickle::Instructions::MEMOIZE" do
|
97
|
+
expect(subject.read_instruction).to be(
|
98
|
+
Python::Pickle::Instructions::MEMOIZE
|
99
|
+
)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "when the opcode is 149" do
|
104
|
+
let(:frame) { "\x8C\x03ABC\x94.".b }
|
105
|
+
let(:length) { frame.bytesize }
|
106
|
+
let(:packed) { [length, frame].pack('Q<a*') }
|
107
|
+
let(:io) { StringIO.new("#{149.chr}#{packed}") }
|
108
|
+
|
109
|
+
it "must return Python::Pickle::Instructions::Frame" do
|
110
|
+
expect(subject.read_instruction).to eq(
|
111
|
+
Python::Pickle::Instructions::Frame.new(length)
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
115
|
+
context "after a Python::Pickle::Instructions::Frame has been returned" do
|
116
|
+
it "must change #io to be a StringIO pointing to the frame's contents" do
|
117
|
+
subject.read_instruction
|
118
|
+
|
119
|
+
expect(subject.io).to be_kind_of(StringIO)
|
120
|
+
expect(subject.io.string).to eq(frame)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "must then read instructions from inside of a frame" do
|
124
|
+
expect(subject.read_instruction).to eq(
|
125
|
+
Python::Pickle::Instructions::Frame.new(length)
|
126
|
+
)
|
127
|
+
|
128
|
+
expect(subject.read_instruction).to eq(
|
129
|
+
Python::Pickle::Instructions::ShortBinUnicode.new(3,'ABC')
|
130
|
+
)
|
131
|
+
|
132
|
+
expect(subject.read_instruction).to eq(
|
133
|
+
Python::Pickle::Instructions::MEMOIZE
|
134
|
+
)
|
135
|
+
|
136
|
+
expect(subject.read_instruction).to eq(
|
137
|
+
Python::Pickle::Instructions::STOP
|
138
|
+
)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|