python-pickle 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.github/workflows/ruby.yml +27 -0
  4. data/.gitignore +5 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +14 -0
  8. data/Gemfile +15 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +149 -0
  11. data/Rakefile +13 -0
  12. data/gemspec.yml +25 -0
  13. data/lib/python/pickle/byte_array.rb +40 -0
  14. data/lib/python/pickle/deserializer.rb +595 -0
  15. data/lib/python/pickle/exceptions.rb +12 -0
  16. data/lib/python/pickle/instruction.rb +52 -0
  17. data/lib/python/pickle/instructions/add_items.rb +26 -0
  18. data/lib/python/pickle/instructions/append.rb +24 -0
  19. data/lib/python/pickle/instructions/appends.rb +26 -0
  20. data/lib/python/pickle/instructions/bin_bytes.rb +32 -0
  21. data/lib/python/pickle/instructions/bin_bytes8.rb +32 -0
  22. data/lib/python/pickle/instructions/bin_float.rb +29 -0
  23. data/lib/python/pickle/instructions/bin_get.rb +27 -0
  24. data/lib/python/pickle/instructions/bin_int1.rb +29 -0
  25. data/lib/python/pickle/instructions/bin_put.rb +29 -0
  26. data/lib/python/pickle/instructions/bin_string.rb +32 -0
  27. data/lib/python/pickle/instructions/bin_unicode.rb +32 -0
  28. data/lib/python/pickle/instructions/bin_unicode8.rb +32 -0
  29. data/lib/python/pickle/instructions/build.rb +24 -0
  30. data/lib/python/pickle/instructions/byte_array8.rb +32 -0
  31. data/lib/python/pickle/instructions/dict.rb +17 -0
  32. data/lib/python/pickle/instructions/dup.rb +24 -0
  33. data/lib/python/pickle/instructions/empty_dict.rb +26 -0
  34. data/lib/python/pickle/instructions/empty_list.rb +26 -0
  35. data/lib/python/pickle/instructions/empty_set.rb +26 -0
  36. data/lib/python/pickle/instructions/empty_tuple.rb +26 -0
  37. data/lib/python/pickle/instructions/ext1.rb +29 -0
  38. data/lib/python/pickle/instructions/ext2.rb +29 -0
  39. data/lib/python/pickle/instructions/ext4.rb +29 -0
  40. data/lib/python/pickle/instructions/float.rb +24 -0
  41. data/lib/python/pickle/instructions/frame.rb +29 -0
  42. data/lib/python/pickle/instructions/frozen_set.rb +26 -0
  43. data/lib/python/pickle/instructions/get.rb +27 -0
  44. data/lib/python/pickle/instructions/global.rb +62 -0
  45. data/lib/python/pickle/instructions/has_length_and_value.rb +58 -0
  46. data/lib/python/pickle/instructions/has_value.rb +50 -0
  47. data/lib/python/pickle/instructions/int.rb +24 -0
  48. data/lib/python/pickle/instructions/list.rb +24 -0
  49. data/lib/python/pickle/instructions/long.rb +24 -0
  50. data/lib/python/pickle/instructions/long1.rb +32 -0
  51. data/lib/python/pickle/instructions/long4.rb +32 -0
  52. data/lib/python/pickle/instructions/long_bin_get.rb +27 -0
  53. data/lib/python/pickle/instructions/mark.rb +24 -0
  54. data/lib/python/pickle/instructions/memoize.rb +26 -0
  55. data/lib/python/pickle/instructions/new_false.rb +24 -0
  56. data/lib/python/pickle/instructions/new_obj.rb +26 -0
  57. data/lib/python/pickle/instructions/new_obj_ex.rb +26 -0
  58. data/lib/python/pickle/instructions/new_true.rb +24 -0
  59. data/lib/python/pickle/instructions/next_buffer.rb +26 -0
  60. data/lib/python/pickle/instructions/none.rb +24 -0
  61. data/lib/python/pickle/instructions/pop.rb +24 -0
  62. data/lib/python/pickle/instructions/pop_mark.rb +24 -0
  63. data/lib/python/pickle/instructions/proto.rb +29 -0
  64. data/lib/python/pickle/instructions/put.rb +24 -0
  65. data/lib/python/pickle/instructions/readonly_buffer.rb +26 -0
  66. data/lib/python/pickle/instructions/reduce.rb +24 -0
  67. data/lib/python/pickle/instructions/set_item.rb +24 -0
  68. data/lib/python/pickle/instructions/set_items.rb +26 -0
  69. data/lib/python/pickle/instructions/short_bin_bytes.rb +32 -0
  70. data/lib/python/pickle/instructions/short_bin_string.rb +32 -0
  71. data/lib/python/pickle/instructions/short_bin_unicode.rb +32 -0
  72. data/lib/python/pickle/instructions/stack_global.rb +26 -0
  73. data/lib/python/pickle/instructions/stop.rb +24 -0
  74. data/lib/python/pickle/instructions/string.rb +24 -0
  75. data/lib/python/pickle/instructions/tuple.rb +24 -0
  76. data/lib/python/pickle/instructions/tuple1.rb +24 -0
  77. data/lib/python/pickle/instructions/tuple2.rb +24 -0
  78. data/lib/python/pickle/instructions/tuple3.rb +24 -0
  79. data/lib/python/pickle/protocol.rb +56 -0
  80. data/lib/python/pickle/protocol0.rb +399 -0
  81. data/lib/python/pickle/protocol1.rb +183 -0
  82. data/lib/python/pickle/protocol2.rb +229 -0
  83. data/lib/python/pickle/protocol3.rb +163 -0
  84. data/lib/python/pickle/protocol4.rb +285 -0
  85. data/lib/python/pickle/protocol5.rb +218 -0
  86. data/lib/python/pickle/py_class.rb +75 -0
  87. data/lib/python/pickle/py_object.rb +141 -0
  88. data/lib/python/pickle/tuple.rb +19 -0
  89. data/lib/python/pickle/version.rb +6 -0
  90. data/lib/python/pickle.rb +226 -0
  91. data/python-pickle.gemspec +62 -0
  92. data/spec/byte_array_spec.rb +54 -0
  93. data/spec/deserializer_spec.rb +1201 -0
  94. data/spec/fixtures/ascii_str_v3.pkl +0 -0
  95. data/spec/fixtures/ascii_str_v4.pkl +0 -0
  96. data/spec/fixtures/ascii_str_v5.pkl +0 -0
  97. data/spec/fixtures/bin_str_v0.pkl +3 -0
  98. data/spec/fixtures/bin_str_v1.pkl +0 -0
  99. data/spec/fixtures/bin_str_v2.pkl +0 -0
  100. data/spec/fixtures/bin_str_v3.pkl +0 -0
  101. data/spec/fixtures/bin_str_v4.pkl +0 -0
  102. data/spec/fixtures/bin_str_v5.pkl +0 -0
  103. data/spec/fixtures/bytearray_v0.pkl +10 -0
  104. data/spec/fixtures/bytearray_v1.pkl +0 -0
  105. data/spec/fixtures/bytearray_v2.pkl +0 -0
  106. data/spec/fixtures/bytearray_v3.pkl +0 -0
  107. data/spec/fixtures/bytearray_v4.pkl +0 -0
  108. data/spec/fixtures/bytearray_v5.pkl +0 -0
  109. data/spec/fixtures/class_v0.pkl +4 -0
  110. data/spec/fixtures/class_v1.pkl +0 -0
  111. data/spec/fixtures/class_v2.pkl +0 -0
  112. data/spec/fixtures/class_v3.pkl +0 -0
  113. data/spec/fixtures/class_v4.pkl +0 -0
  114. data/spec/fixtures/class_v5.pkl +0 -0
  115. data/spec/fixtures/dict_v0.pkl +6 -0
  116. data/spec/fixtures/dict_v1.pkl +0 -0
  117. data/spec/fixtures/dict_v2.pkl +0 -0
  118. data/spec/fixtures/dict_v3.pkl +0 -0
  119. data/spec/fixtures/dict_v4.pkl +0 -0
  120. data/spec/fixtures/dict_v5.pkl +0 -0
  121. data/spec/fixtures/escaped_str_v0.pkl +3 -0
  122. data/spec/fixtures/escaped_str_v1.pkl +0 -0
  123. data/spec/fixtures/escaped_str_v2.pkl +0 -0
  124. data/spec/fixtures/false_v0.pkl +2 -0
  125. data/spec/fixtures/false_v1.pkl +2 -0
  126. data/spec/fixtures/false_v2.pkl +1 -0
  127. data/spec/fixtures/false_v3.pkl +1 -0
  128. data/spec/fixtures/false_v4.pkl +1 -0
  129. data/spec/fixtures/false_v5.pkl +1 -0
  130. data/spec/fixtures/float_v0.pkl +2 -0
  131. data/spec/fixtures/float_v1.pkl +1 -0
  132. data/spec/fixtures/float_v2.pkl +1 -0
  133. data/spec/fixtures/float_v3.pkl +1 -0
  134. data/spec/fixtures/float_v4.pkl +0 -0
  135. data/spec/fixtures/float_v5.pkl +0 -0
  136. data/spec/fixtures/function_v0.pkl +4 -0
  137. data/spec/fixtures/function_v1.pkl +0 -0
  138. data/spec/fixtures/function_v2.pkl +0 -0
  139. data/spec/fixtures/function_v3.pkl +0 -0
  140. data/spec/fixtures/function_v4.pkl +0 -0
  141. data/spec/fixtures/function_v5.pkl +0 -0
  142. data/spec/fixtures/hex_str_v0.pkl +3 -0
  143. data/spec/fixtures/hex_str_v1.pkl +0 -0
  144. data/spec/fixtures/hex_str_v2.pkl +0 -0
  145. data/spec/fixtures/int_v0.pkl +2 -0
  146. data/spec/fixtures/int_v1.pkl +1 -0
  147. data/spec/fixtures/int_v2.pkl +1 -0
  148. data/spec/fixtures/int_v3.pkl +1 -0
  149. data/spec/fixtures/int_v4.pkl +1 -0
  150. data/spec/fixtures/int_v5.pkl +1 -0
  151. data/spec/fixtures/list_v0.pkl +7 -0
  152. data/spec/fixtures/list_v1.pkl +0 -0
  153. data/spec/fixtures/list_v2.pkl +0 -0
  154. data/spec/fixtures/list_v3.pkl +0 -0
  155. data/spec/fixtures/list_v4.pkl +0 -0
  156. data/spec/fixtures/list_v5.pkl +0 -0
  157. data/spec/fixtures/long_v0.pkl +2 -0
  158. data/spec/fixtures/long_v1.pkl +2 -0
  159. data/spec/fixtures/long_v2.pkl +0 -0
  160. data/spec/fixtures/long_v3.pkl +0 -0
  161. data/spec/fixtures/long_v4.pkl +0 -0
  162. data/spec/fixtures/long_v5.pkl +0 -0
  163. data/spec/fixtures/nested_dict_v0.pkl +12 -0
  164. data/spec/fixtures/nested_dict_v1.pkl +0 -0
  165. data/spec/fixtures/nested_dict_v2.pkl +0 -0
  166. data/spec/fixtures/nested_dict_v3.pkl +0 -0
  167. data/spec/fixtures/nested_dict_v4.pkl +0 -0
  168. data/spec/fixtures/nested_dict_v5.pkl +0 -0
  169. data/spec/fixtures/nested_list_v0.pkl +9 -0
  170. data/spec/fixtures/nested_list_v1.pkl +0 -0
  171. data/spec/fixtures/nested_list_v2.pkl +0 -0
  172. data/spec/fixtures/nested_list_v3.pkl +0 -0
  173. data/spec/fixtures/nested_list_v4.pkl +0 -0
  174. data/spec/fixtures/nested_list_v5.pkl +0 -0
  175. data/spec/fixtures/none_v0.pkl +1 -0
  176. data/spec/fixtures/none_v1.pkl +1 -0
  177. data/spec/fixtures/none_v2.pkl +1 -0
  178. data/spec/fixtures/none_v3.pkl +1 -0
  179. data/spec/fixtures/none_v4.pkl +1 -0
  180. data/spec/fixtures/none_v5.pkl +1 -0
  181. data/spec/fixtures/object_v0.pkl +19 -0
  182. data/spec/fixtures/object_v1.pkl +0 -0
  183. data/spec/fixtures/object_v2.pkl +0 -0
  184. data/spec/fixtures/object_v3.pkl +0 -0
  185. data/spec/fixtures/object_v4.pkl +0 -0
  186. data/spec/fixtures/object_v5.pkl +0 -0
  187. data/spec/fixtures/str_v0.pkl +3 -0
  188. data/spec/fixtures/str_v1.pkl +0 -0
  189. data/spec/fixtures/str_v2.pkl +0 -0
  190. data/spec/fixtures/str_v3.pkl +0 -0
  191. data/spec/fixtures/str_v4.pkl +0 -0
  192. data/spec/fixtures/str_v5.pkl +0 -0
  193. data/spec/fixtures/true_v0.pkl +2 -0
  194. data/spec/fixtures/true_v1.pkl +2 -0
  195. data/spec/fixtures/true_v2.pkl +1 -0
  196. data/spec/fixtures/true_v3.pkl +1 -0
  197. data/spec/fixtures/true_v4.pkl +1 -0
  198. data/spec/fixtures/true_v5.pkl +1 -0
  199. data/spec/fixtures/unicode_str_v0.pkl +3 -0
  200. data/spec/fixtures/unicode_str_v1.pkl +0 -0
  201. data/spec/fixtures/unicode_str_v2.pkl +0 -0
  202. data/spec/fixtures/unicode_str_v3.pkl +0 -0
  203. data/spec/fixtures/unicode_str_v4.pkl +0 -0
  204. data/spec/fixtures/unicode_str_v5.pkl +0 -0
  205. data/spec/generate_pickles2.py +41 -0
  206. data/spec/generate_pickles3.py +40 -0
  207. data/spec/integration/load/protocol0_spec.rb +258 -0
  208. data/spec/integration/load/protocol1_spec.rb +258 -0
  209. data/spec/integration/load/protocol2_spec.rb +258 -0
  210. data/spec/integration/load/protocol3_spec.rb +258 -0
  211. data/spec/integration/load/protocol4_spec.rb +258 -0
  212. data/spec/integration/load/protocol5_spec.rb +258 -0
  213. data/spec/integration/parse/protocol0_spec.rb +467 -0
  214. data/spec/integration/parse/protocol1_spec.rb +459 -0
  215. data/spec/integration/parse/protocol2_spec.rb +471 -0
  216. data/spec/integration/parse/protocol3_spec.rb +407 -0
  217. data/spec/integration/parse/protocol4_spec.rb +439 -0
  218. data/spec/integration/parse/protocol5_spec.rb +419 -0
  219. data/spec/pickle_spec.rb +163 -0
  220. data/spec/protocol0_read_instruction_examples.rb +211 -0
  221. data/spec/protocol0_spec.rb +445 -0
  222. data/spec/protocol1_read_instruction_examples.rb +156 -0
  223. data/spec/protocol1_spec.rb +59 -0
  224. data/spec/protocol2_read_instruction_examples.rb +135 -0
  225. data/spec/protocol2_spec.rb +128 -0
  226. data/spec/protocol3_read_instruction_examples.rb +29 -0
  227. data/spec/protocol3_spec.rb +32 -0
  228. data/spec/protocol4_read_instruction_examples.rb +142 -0
  229. data/spec/protocol4_spec.rb +58 -0
  230. data/spec/protocol5_spec.rb +68 -0
  231. data/spec/py_class_spec.rb +62 -0
  232. data/spec/py_object_spec.rb +149 -0
  233. data/spec/spec_helper.rb +3 -0
  234. data/spec/tuple_spec.rb +18 -0
  235. 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