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.
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