1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
|
# frozen_string_literal: true
require_relative "elements"
module Gem
module SafeMarshal
class Reader
class Error < StandardError
end
class UnsupportedVersionError < Error
end
class UnconsumedBytesError < Error
end
class NotImplementedError < Error
end
def initialize(io)
@io = io
end
def read!
read_header
root = read_element
raise UnconsumedBytesError unless @io.eof?
root
end
private
MARSHAL_VERSION = [Marshal::MAJOR_VERSION, Marshal::MINOR_VERSION].map(&:chr).join.freeze
private_constant :MARSHAL_VERSION
def read_header
v = @io.read(2)
raise UnsupportedVersionError, "Unsupported marshal version #{v.bytes.map(&:ord).join(".")}, expected #{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}" unless v == MARSHAL_VERSION
end
def read_byte
@io.getbyte
end
def read_integer
b = read_byte
case b
when 0x00
0
when 0x01
@io.read(1).unpack1("C")
when 0x02
@io.read(2).unpack1("S<")
when 0x03
(@io.read(3) + "\0").unpack1("L<")
when 0x04
@io.read(4).unpack1("L<")
when 0xFC
@io.read(4).unpack1("L<") | -0x100000000
when 0xFD
(@io.read(3) + "\0").unpack1("L<") | -0x1000000
when 0xFE
@io.read(2).unpack1("s<") | -0x10000
when 0xFF
read_byte | -0x100
else
signed = (b ^ 128) - 128
if b >= 128
signed + 5
else
signed - 5
end
end
end
def read_element
type = read_byte
case type
when 34 then read_string # ?"
when 48 then read_nil # ?0
when 58 then read_symbol # ?:
when 59 then read_symbol_link # ?;
when 64 then read_object_link # ?@
when 70 then read_false # ?F
when 73 then read_object_with_ivars # ?I
when 84 then read_true # ?T
when 85 then read_user_marshal # ?U
when 91 then read_array # ?[
when 102 then read_float # ?f
when 105 then Elements::Integer.new int: read_integer # ?i
when 108 then read_bignum # ?l
when 111 then read_object # ?o
when 117 then read_user_defined # ?u
when 123 then read_hash # ?{
when 125 then read_hash_with_default_value # ?}
when "e".ord then read_extended_object
when "c".ord then read_class
when "m".ord then read_module
when "M".ord then read_class_or_module
when "d".ord then read_data
when "/".ord then read_regexp
when "S".ord then read_struct
when "C".ord then read_user_class
else
raise Error, "Unknown marshal type discriminator #{type.chr.inspect} (#{type})"
end
end
def read_symbol
Elements::Symbol.new name: @io.read(read_integer)
end
def read_string
Elements::String.new(str: @io.read(read_integer))
end
def read_true
Elements::True::TRUE
end
def read_false
Elements::False::FALSE
end
def read_user_defined
Elements::UserDefined.new(name: read_element, binary_string: @io.read(read_integer))
end
def read_array
Elements::Array.new(elements: Array.new(read_integer) do |_i|
read_element
end)
end
def read_object_with_ivars
Elements::WithIvars.new(object: read_element, ivars:
Array.new(read_integer) do
[read_element, read_element]
end)
end
def read_symbol_link
Elements::SymbolLink.new offset: read_integer
end
def read_user_marshal
Elements::UserMarshal.new(name: read_element, data: read_element)
end
def read_object_link
Elements::ObjectLink.new(offset: read_integer)
end
def read_hash
pairs = Array.new(read_integer) do
[read_element, read_element]
end
Elements::Hash.new(pairs: pairs)
end
def read_hash_with_default_value
pairs = Array.new(read_integer) do
[read_element, read_element]
end
Elements::HashWithDefaultValue.new(pairs: pairs, default: read_element)
end
def read_object
Elements::WithIvars.new(
object: Elements::Object.new(name: read_element),
ivars: Array.new(read_integer) do
[read_element, read_element]
end
)
end
def read_nil
Elements::Nil::NIL
end
def read_float
Elements::Float.new string: @io.read(read_integer)
end
def read_bignum
Elements::Bignum.new(sign: read_byte, data: @io.read(read_integer * 2))
end
def read_extended_object
raise NotImplementedError, "Reading Marshal objects of type extended_object is not implemented"
end
def read_class
raise NotImplementedError, "Reading Marshal objects of type class is not implemented"
end
def read_module
raise NotImplementedError, "Reading Marshal objects of type module is not implemented"
end
def read_class_or_module
raise NotImplementedError, "Reading Marshal objects of type class_or_module is not implemented"
end
def read_data
raise NotImplementedError, "Reading Marshal objects of type data is not implemented"
end
def read_regexp
raise NotImplementedError, "Reading Marshal objects of type regexp is not implemented"
end
def read_struct
raise NotImplementedError, "Reading Marshal objects of type struct is not implemented"
end
def read_user_class
raise NotImplementedError, "Reading Marshal objects of type user_class is not implemented"
end
end
end
end
|