python-pickle 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|